From 948914e88fa6849ec3be1cd88113dc7febeda577 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 22 Aug 2024 14:16:57 +0700 Subject: update template detail product --- package.json | 3 +- .../product-detail/components/Information.tsx | 126 ++++++++++--- .../product-detail/components/PriceAction.tsx | 45 +++-- .../product-detail/components/ProductDetail.tsx | 194 +++++++++++---------- .../product-detail/stores/useProductDetail.ts | 12 ++ src/pages/_app.jsx | 2 +- 6 files changed, 242 insertions(+), 140 deletions(-) diff --git a/package.json b/package.json index 28fbc5d8..98d4077c 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "dependencies": { "@chakra-ui/next-js": "^2.1.5", "@chakra-ui/react": "^2.8.1", + "@choc-ui/chakra-autocomplete": "^5.6.2", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", "@heroicons/react": "^2.0.13", @@ -25,7 +26,7 @@ "clsx": "^2.0.0", "cookies-next": "^2.1.1", "flowbite": "^1.6.4", - "framer-motion": "^7.10.3", + "framer-motion": "^11.3.28", "http-proxy-middleware": "^3.0.0", "lodash-contrib": "^4.1200.1", "lucide-react": "^0.279.0", diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 75ae3c41..5d70e534 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -1,56 +1,130 @@ -import style from '../styles/information.module.css' +import style from '../styles/information.module.css'; +import { + AutoComplete, + AutoCompleteInput, + AutoCompleteItem, + AutoCompleteList, +} from '@choc-ui/chakra-autocomplete'; -import React from 'react' -import dynamic from 'next/dynamic' -import Link from 'next/link' -import { useQuery } from 'react-query' +import React, { useEffect, useState } from 'react'; +import dynamic from 'next/dynamic'; +import Link from 'next/link'; +import { useQuery } from 'react-query'; -import { IProductDetail } from '~/types/product' -import { IProductVariantSLA } from '~/types/productVariant' -import { createSlug } from '~/libs/slug' -import { getVariantSLA } from '~/services/productVariant' -import { formatToShortText } from '~/libs/formatNumber' +import { IProductDetail } from '~/types/product'; +import { IProductVariantSLA } from '~/types/productVariant'; +import { createSlug } from '~/libs/slug'; +import { getVariantSLA } from '~/services/productVariant'; +import { formatToShortText } from '~/libs/formatNumber'; +import currencyFormat from '@/core/utils/currencyFormat'; +import { Icon, InputGroup, InputRightElement } from '@chakra-ui/react'; +import { CheckIcon, ChevronDownIcon, FingerPrintIcon } from '@heroicons/react/24/outline'; +import { useProductDetail } from '../stores/useProductDetail'; -const Skeleton = dynamic(() => import('@chakra-ui/react').then((mod) => mod.Skeleton)) +const Skeleton = dynamic(() => + import('@chakra-ui/react').then((mod) => mod.Skeleton) +); type Props = { - product: IProductDetail -} + product: IProductDetail; +}; const Information = ({ product }: Props) => { + const { selectedVariant, setSelectedVariant, setSla, setActive, } = useProductDetail() + const variantOptions = product?.variants; + const querySLA = useQuery({ - queryKey: ['variant-sla', product.variants[0]?.id], - queryFn: () => getVariantSLA(product.variants[0].id), - enabled: product.variant_total === 1 - }) + queryKey: ['variant-sla', selectedVariant?.id], + queryFn: () => getVariantSLA(selectedVariant?.id), + enabled: selectedVariant?.id === 1, + }); + const sla = querySLA?.data; + + useEffect(() => { + setSla(querySLA?.data); + }, [selectedVariant]); - const sla = querySLA?.data + const handleOnChange = (vals: any) => { + let code = vals.split(" ")[0]; + let variant = variantOptions.find((item) => item.code === code); + setSelectedVariant(variant); + } return (
+
+ + handleOnChange(vals)}> + + + + + + + + + {variantOptions.map((option, cid) => ( + +
+
+ {option.code + ' - ' + option.attributes[0]} +
+
+
+ {Math.floor(option?.price?.discount_percentage)}% +
+
+ {currencyFormat(option?.price?.price)} +
+
+ {currencyFormat(option?.price?.price_discount)} +
+
+
+
+ ))} +
+
+
-
SKU Number
-
SKU-{product.id}
+
Item Code
+
{selectedVariant?.code}
Manufacture
{!!product.manufacture.name ? ( {product.manufacture.name} - ) : '-'} + ) : ( + '-' + )}
Terjual
-
{product.qty_sold > 0 ? formatToShortText(product.qty_sold) : '-'}
+
+ {product.qty_sold > 0 ? formatToShortText(product.qty_sold) : '-'} +
+
+
+
Persiapan Barang
+
{sla?.sla_date}
- ) -} + ); +}; -export default Information \ No newline at end of file +export default Information; diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 81271f6e..405eb12b 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -22,23 +22,22 @@ const PriceAction = ({ product }: Props) => { askAdminUrl, isApproval, setIsApproval, + selectedVariant, + sla, } = useProductDetail(); useEffect(() => { - setActive(product.variants[0]) - if(product.variants.length > 2 && product.variants[0].price.price === 0){ - const variants = product.variants + setActive(selectedVariant); + if (product.variants.length > 2 && product.variants[0].price.price === 0) { + const variants = product.variants; for (let i = 0; i < variants.length; i++) { - if(variants[i].price.price > 0){ - setActive(variants[i]) + if (variants[i].price.price > 0) { + setActive(variants[i]); break; } } } - - }, [product, setActive]); - - + }, [product, setActive, selectedVariant]); return (
{ )} +
+
+
+ + setQuantityInput(e.target.value)} + className={style['quantity-input']} + /> +
+
+ Stock : {sla?.qty} +
+
- - setQuantityInput(e.target.value)} - className={style['quantity-input']} - /> { - const { isDesktop, isMobile } = useDevice() - const router = useRouter() - const auth = getAuth() - const { setAskAdminUrl, askAdminUrl, activeVariantId, setIsApproval, isApproval } = useProductDetail() + const { isDesktop, isMobile } = useDevice(); + const router = useRouter(); + const auth = getAuth(); + const { + setAskAdminUrl, + askAdminUrl, + activeVariantId, + setIsApproval, + isApproval, + setSelectedVariant, + } = useProductDetail(); useEffect(() => { gtagProductDetail(product); - },[product]) + }, [product]); useEffect(() => { const createdAskUrl = whatsappUrl({ @@ -48,76 +55,43 @@ const ProductDetail = ({ product }: Props) => { payload: { manufacture: product.manufacture.name, productName: product.name, - url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath + url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath, }, - fallbackUrl: router.asPath - }) + fallbackUrl: router.asPath, + }); - setAskAdminUrl(createdAskUrl) - }, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl]) + setAskAdminUrl(createdAskUrl); + }, [router.asPath, product.manufacture.name, product.name, setAskAdminUrl]); useEffect(() => { if (typeof auth === 'object') { setIsApproval(auth?.feature?.soApproval); } + setSelectedVariant(product?.variants[0]) }, []); return ( <>
-
+
-
+
-

- {product.name} -

+

{product.name}

- -
- - - - - - - -
-
@@ -129,7 +103,9 @@ const ProductDetail = ({ product }: Props) => { )}
- {!!activeVariantId && !isApproval && } + {!!activeVariantId && !isApproval && ( + + )}

@@ -142,27 +118,61 @@ const ProductDetail = ({ product }: Props) => {
-

- Informasi Produk -

+

Informasi Produk


' ? 'Belum ada deskripsi' : product.description }} + dangerouslySetInnerHTML={{ + __html: + !product.description || product.description == '


' + ? 'Belum ada deskripsi' + : product.description, + }} />
{isDesktop && ( -
+
+
+ + + | + + + + | + + + + +
- -
- Produk Serupa -
+
Produk Serupa
@@ -171,9 +181,7 @@ const ProductDetail = ({ product }: Props) => { )}
-
- Kamu Mungkin Juga Suka -
+
Kamu Mungkin Juga Suka
@@ -185,7 +193,7 @@ const ProductDetail = ({ product }: Props) => {
- ) -} + ); +}; -export default ProductDetail \ No newline at end of file +export default ProductDetail; diff --git a/src-migrate/modules/product-detail/stores/useProductDetail.ts b/src-migrate/modules/product-detail/stores/useProductDetail.ts index eb409930..dee6b342 100644 --- a/src-migrate/modules/product-detail/stores/useProductDetail.ts +++ b/src-migrate/modules/product-detail/stores/useProductDetail.ts @@ -7,6 +7,8 @@ type State = { quantityInput: string; askAdminUrl: string; isApproval : boolean; + selectedVariant : any; + sla : any; }; type Action = { @@ -14,6 +16,8 @@ type Action = { setQuantityInput: (value: string) => void; setAskAdminUrl: (url: string) => void; setIsApproval : (value : boolean) => void; + setSelectedVariant : (value : any) => void; + setSla : (value : any) => void; }; export const useProductDetail = create((set, get) => ({ @@ -22,6 +26,8 @@ export const useProductDetail = create((set, get) => ({ quantityInput: '1', askAdminUrl: '', isApproval : false, + selectedVariant: null, + sla : null, setActive: (variant) => { set({ activeVariantId: variant?.id, activePrice: variant?.price }); }, @@ -33,5 +39,11 @@ export const useProductDetail = create((set, get) => ({ }, setIsApproval : (value : boolean) => { set({ isApproval : value }) + }, + setSelectedVariant : (value : any) => { + set({ selectedVariant : value }) + }, + setSla : (value : any ) => { + set({ sla : value }) } })); diff --git a/src/pages/_app.jsx b/src/pages/_app.jsx index bcb41dd6..f52aa5f7 100644 --- a/src/pages/_app.jsx +++ b/src/pages/_app.jsx @@ -85,7 +85,7 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) { return ( - + {animateLoader && ( Date: Mon, 26 Aug 2024 10:47:28 +0700 Subject: new product template --- .../product-detail/components/Information.tsx | 41 +++++++++++++++++----- .../product-detail/components/PriceAction.tsx | 33 ++++++++++++----- .../product-detail/components/ProductDetail.tsx | 4 +-- .../product-detail/styles/price-action.module.css | 2 +- src-migrate/types/product.ts | 1 + 5 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 5d70e534..c7f21d62 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -18,7 +18,11 @@ import { getVariantSLA } from '~/services/productVariant'; import { formatToShortText } from '~/libs/formatNumber'; import currencyFormat from '@/core/utils/currencyFormat'; import { Icon, InputGroup, InputRightElement } from '@chakra-ui/react'; -import { CheckIcon, ChevronDownIcon, FingerPrintIcon } from '@heroicons/react/24/outline'; +import { + CheckIcon, + ChevronDownIcon, + FingerPrintIcon, +} from '@heroicons/react/24/outline'; import { useProductDetail } from '../stores/useProductDetail'; const Skeleton = dynamic(() => @@ -30,39 +34,52 @@ type Props = { }; const Information = ({ product }: Props) => { - const { selectedVariant, setSelectedVariant, setSla, setActive, } = useProductDetail() + const { selectedVariant, setSelectedVariant, setSla, setActive } = + useProductDetail(); const variantOptions = product?.variants; const querySLA = useQuery({ queryKey: ['variant-sla', selectedVariant?.id], queryFn: () => getVariantSLA(selectedVariant?.id), - enabled: selectedVariant?.id === 1, + refetchOnWindowFocus: false, }); const sla = querySLA?.data; + console.log('sla', querySLA, selectedVariant); useEffect(() => { setSla(querySLA?.data); }, [selectedVariant]); const handleOnChange = (vals: any) => { - let code = vals.split(" ")[0]; + let code = vals.split(' ')[0]; let variant = variantOptions.find((item) => item.code === code); setSelectedVariant(variant); - } + }; return (
- - handleOnChange(vals)}> + + handleOnChange(vals)} + > - + - + {variantOptions.map((option, cid) => ( { )}
+
+
Berat Barang
+
+ {selectedVariant?.weight > 0 ? `${selectedVariant?.weight} Kg` : '-'} +
+
Terjual
diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 405eb12b..b46afac9 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -39,6 +39,18 @@ const PriceAction = ({ product }: Props) => { } }, [product, setActive, selectedVariant]); + let voucherPastiHemat = 0; + + if ( + product?.voucher_pasti_hemat + ? product?.voucher_pasti_hemat.length + : voucherPastiHemat > 0 + ) { + const stringVoucher = product?.voucher_pasti_hemat[0]; + const validJsonString = stringVoucher.replace(/'/g, '"'); + voucherPastiHemat = JSON.parse(validJsonString); + } + return (
{ - setQuantityInput(e.target.value)} - className={style['quantity-input']} - /> +
+ setQuantityInput(e.target.value)} + className={style['quantity-input']} + /> +
- Stock : {sla?.qty} + + {' '} + Stock : {sla?.qty}{' '} +
diff --git a/src-migrate/modules/product-detail/components/ProductDetail.tsx b/src-migrate/modules/product-detail/components/ProductDetail.tsx index 0997466c..c19c288a 100644 --- a/src-migrate/modules/product-detail/components/ProductDetail.tsx +++ b/src-migrate/modules/product-detail/components/ProductDetail.tsx @@ -107,13 +107,13 @@ const ProductDetail = ({ product }: Props) => { )} -
+ {/*

Variant ({product.variant_total})

-
+
*/}
diff --git a/src-migrate/modules/product-detail/styles/price-action.module.css b/src-migrate/modules/product-detail/styles/price-action.module.css index 651de958..de69f5f1 100644 --- a/src-migrate/modules/product-detail/styles/price-action.module.css +++ b/src-migrate/modules/product-detail/styles/price-action.module.css @@ -8,7 +8,7 @@ @apply flex gap-x-2.5; } .quantity-input { - @apply px-2 rounded text-center border border-gray-300 w-14 h-10 focus:outline-none; + @apply px-2 rounded text-center border border-gray-300 w-24 h-10 focus:outline-none; } .contact-us { diff --git a/src-migrate/types/product.ts b/src-migrate/types/product.ts index 681cdc8e..927a0ca3 100644 --- a/src-migrate/types/product.ts +++ b/src-migrate/types/product.ts @@ -31,6 +31,7 @@ export interface IProduct { id: number; name: string; }; + voucher_pasti_hemat : any; } export interface IProductDetail extends IProduct { -- cgit v1.2.3 From 914729a0d6ba9a9dd32d308954642439fa88c1d2 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Mon, 9 Sep 2024 13:37:36 +0700 Subject: add manufacture logd --- .../product-detail/components/Information.tsx | 40 ++++++++++++++-------- src-migrate/types/product.ts | 1 + src/utils/solrMapping.js | 3 +- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index c7f21d62..5df81dae 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -24,6 +24,7 @@ import { FingerPrintIcon, } from '@heroicons/react/24/outline'; import { useProductDetail } from '../stores/useProductDetail'; +import Image from 'next/image'; const Skeleton = dynamic(() => import('@chakra-ui/react').then((mod) => mod.Skeleton) @@ -34,20 +35,24 @@ type Props = { }; const Information = ({ product }: Props) => { - const { selectedVariant, setSelectedVariant, setSla, setActive } = + const { selectedVariant, setSelectedVariant, setSla, setActive, sla } = useProductDetail(); const variantOptions = product?.variants; - const querySLA = useQuery({ - queryKey: ['variant-sla', selectedVariant?.id], - queryFn: () => getVariantSLA(selectedVariant?.id), - refetchOnWindowFocus: false, - }); - const sla = querySLA?.data; - console.log('sla', querySLA, selectedVariant); + // const querySLA = useQuery({ + // queryKey: ['variant-sla', selectedVariant?.id], + // queryFn: () => getVariantSLA(selectedVariant?.id), + // enabled: !!selectedVariant?.id, + // }); + // const sla = querySLA?.data; + + const getsla = async () => { + const querySLA = await getVariantSLA(selectedVariant?.id); + setSla(querySLA); + }; useEffect(() => { - setSla(querySLA?.data); + getsla(); }, [selectedVariant]); const handleOnChange = (vals: any) => { @@ -71,15 +76,17 @@ const Information = ({ product }: Props) => { onChange={(vals) => handleOnChange(vals)} > - + } + /> - + {variantOptions.map((option, cid) => ( {
+
Item Code
{selectedVariant?.code}
@@ -121,9 +129,13 @@ const Information = ({ product }: Props) => { product.manufacture.name, product.manufacture.id.toString() )} - className='text-danger-500 hover:underline' > - {product.manufacture.name} + ) : ( '-' diff --git a/src-migrate/types/product.ts b/src-migrate/types/product.ts index 3854f304..fb9c888c 100644 --- a/src-migrate/types/product.ts +++ b/src-migrate/types/product.ts @@ -31,6 +31,7 @@ export interface IProduct { manufacture: { id: number; name: string; + logo: string; }; voucher_pasti_hemat : any; } diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index 0d50b99b..1618c40a 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -65,7 +65,7 @@ export const productMappingSolr = (products, pricelist) => { isTkdn:product?.tkdn_b || false, isSni:product?.sni_b || false, is_in_bu:product?.is_in_bu_b || false, - voucherPastiHemat:product?.voucher_pastihemat || [] + voucherPastiHemat:product?.voucher_pastihemat || [], }; if (product.manufacture_id_i && product.manufacture_name_s) { @@ -74,6 +74,7 @@ export const productMappingSolr = (products, pricelist) => { name: product.manufacture_name_s || '', imagePromotion1: product.image_promotion_1_s || '', imagePromotion2: product.image_promotion_2_s || '', + logo : product.x_logo_manufacture_s || '', }; } -- cgit v1.2.3 From 1475593324319d1faf377f2d00a22a4b3caa3faa Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 12 Sep 2024 15:08:21 +0700 Subject: price action --- .../product-detail/components/Information.tsx | 40 +++++++++++++++------- .../product-detail/components/PriceAction.tsx | 18 ++++++++-- .../product-detail/components/ProductDetail.tsx | 2 +- src/core/components/layouts/BasicLayout.jsx | 33 +++++++++++++----- 4 files changed, 69 insertions(+), 24 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 5df81dae..075ff8d1 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -25,6 +25,8 @@ import { } from '@heroicons/react/24/outline'; import { useProductDetail } from '../stores/useProductDetail'; import Image from 'next/image'; +import useDevice from '@/core/hooks/useDevice'; +import { optimizedAppearDataAttribute } from 'framer-motion'; const Skeleton = dynamic(() => import('@chakra-ui/react').then((mod) => mod.Skeleton) @@ -37,6 +39,10 @@ type Props = { const Information = ({ product }: Props) => { const { selectedVariant, setSelectedVariant, setSla, setActive, sla } = useProductDetail(); + + const [inputValue, setInputValue] = useState( + selectedVariant?.code + ' - ' + selectedVariant?.attributes[0] + ); const variantOptions = product?.variants; // const querySLA = useQuery({ @@ -59,12 +65,17 @@ const Information = ({ product }: Props) => { let code = vals.split(' ')[0]; let variant = variantOptions.find((item) => item.code === code); setSelectedVariant(variant); + setInputValue(variant?.code + ' - ' + variant?.attributes[0]); + }; + + const handleOnKeyUp = (e: any) => { + setInputValue(e.target.value); }; return (
-

)}
- - + setQuantityInput(e.target.value)} + className=' w-24 h-10 text-center border border-gray-300 rounded focus:outline-none' + /> + +
+
+
+ - +
- +
+ + + | + + + + | + + + + +
-- cgit v1.2.3 From ca30c28dd0b19977eb771fc32ff5e520cdef1068 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 3 Oct 2024 16:47:37 +0700 Subject: product varian --- src-migrate/pages/shop/cart/index.tsx | 10 +- .../components/Product/ProductDesktopVariant.jsx | 185 +++++++++++---------- src/pages/api/shop/variant-detail.js | 2 +- src/pages/shop/product/variant/[slug].jsx | 7 - src/utils/solrMapping.js | 1 + 5 files changed, 109 insertions(+), 96 deletions(-) diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index c5386c91..70a28073 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -35,6 +35,8 @@ const CartPage = () => { const [hasChanged, setHasChanged] = useState(false); const prevCartRef = useRef(null); + console.log('ini cart', cart); + useEffect(() => { const handleScroll = () => { setIsTop(window.scrollY < 200); @@ -84,19 +86,19 @@ const CartPage = () => { const hasSelectedPromo = useMemo(() => { if (!cart) return false; - return cart.products.some( + return cart?.products?.some( (item) => item.cart_type === 'promotion' && item.selected ); }, [cart]); const hasSelected = useMemo(() => { if (!cart) return false; - return cart.products.some((item) => item.selected); + return cart?.products?.some((item) => item.selected); }, [cart]); const hasSelectNoPrice = useMemo(() => { if (!cart) return false; - return cart.products.some( + return cart?.products?.some( (item) => item.selected && item.price.price_discount === 0 ); }, [cart]); @@ -230,7 +232,7 @@ const CartPage = () => {
- {cart?.products.map((item) => ( + {cart?.products?.map((item) => ( ))} diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index 2f2caa56..55effdfb 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -18,10 +18,13 @@ import currencyFormat from '@/core/utils/currencyFormat'; import { createSlug } from '@/core/utils/slug'; import whatsappUrl from '@/core/utils/whatsappUrl'; +import { RWebShare } from 'react-web-share'; import productSimilarApi from '../../api/productSimilarApi'; import ProductCard from '../ProductCard'; import ProductSimilar from '../ProductSimilar'; -import { RWebShare } from 'react-web-share'; +import ProductPromoSection from '~/modules/product-promo/components/Section'; + +const SELF_HOST = process.env.NEXT_PUBLIC_SELF_HOST; const ProductDesktopVariant = ({ product, @@ -44,11 +47,18 @@ const ProductDesktopVariant = ({ const [quantityInput, setQuantityInput] = useState(1); + const createdAskUrl = whatsappUrl({ + template: 'product', + payload: { + manufacture: product.manufacture.name, + productName: product.name, + url: process.env.NEXT_PUBLIC_SELF_HOST + router.asPath, + }, + fallbackUrl: router.asPath, + }); + const getLowestPrice = useCallback(() => { const lowest = product.price; - /* const lowest = prices.reduce((lowest, price) => { - return price.priceDiscount < lowest.priceDiscount ? price : lowest - }, prices[0])*/ return lowest; }, [product]); @@ -81,7 +91,7 @@ const ProductDesktopVariant = ({ router.push(`/login?next=/shop/product/${slug}`); return; } - const quantity = variantQuantityRefs.current[product.id].value; + const quantity = quantityInput; if (!validQuantity(quantity)) return; updateItemCart({ productId: product.id, @@ -96,7 +106,7 @@ const ProductDesktopVariant = ({ }; const handleBuy = (variant) => { - const quantity = variantQuantityRefs.current[product.id].value; + const quantity = quantityInput; if (!validQuantity(quantity)) return; updateItemCart({ @@ -177,71 +187,26 @@ const ProductDesktopVariant = ({
Item Code
{product.code}
-
+
Manufacture
- {product.manufacture?.name ? ( - - {product.manufacture?.name} - - ) : ( -
-
- )} -
-
- -
-
- Persiapan Barang -
-
- {!product?.sla && } - {product?.sla && ( - - - {product?.sla?.slaDate} - - - - )} + + {product.manufacture.name} +
- {/*
*/} - -
+
Berat Barang
{product?.weight > 0 && {product?.weight} KG} @@ -263,24 +228,52 @@ const ProductDesktopVariant = ({ )}
+
+
Terjual
+
-
+
+ +
+
+ Persiapan Barang +
+
+ {!product?.sla && } + {product?.sla && ( + + + {product?.sla?.slaDate} + + + + )} +
+
-
-

- Informasi Produk -

-
-

' - ? 'Belum ada deskripsi' - : product.parent.description, - }} - /> +
+ + +
+

+ Informasi Produk +

+
+

' + ? 'Belum ada deskripsi' + : product.parent.description, + }} + /> +
@@ -341,7 +334,7 @@ const ProductDesktopVariant = ({ )} )} -
+
+
+ + {' '} + Stock : {product?.sla?.qty}{' '} + +
+
+ {product?.sla?.qty > 0 && ( + + pickup now + + )} +
-
+
-
+
+ { + setAddCartAlert(false); + }} + > +
+
+ +
+
+ {!!product.manufacture.name ? ( + + {product.manufacture.name} + + ) : ( + '-' + )} +

{product.name}

+

{product.code}

+ {!!product.lowest_price && product.lowest_price.price > 0 && ( + <> +
+ {product.lowest_price.discount_percentage > 0 && ( + <> +
+ {Math.floor(product.lowest_price.discount_percentage)}% +
+
+ Rp {formatCurrency(product.lowest_price.price || 0)} +
+ + )} +
+ Rp{' '} + {formatCurrency(product.lowest_price.price_discount || 0)} +
+
+ + )} + + {!!product.lowest_price && product.lowest_price.price === 0 && ( + + Hubungi kami untuk dapatkan harga terbaik,{' '} + + klik disini + + + )} +
+
+ + Lihat Keranjang + +
+
+
+
+ Kamu Mungkin Juga Suka +
+ + + +
+
+
+ ); +}; + +export default AddToQuotation; diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 9021264e..a69e896c 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -5,6 +5,7 @@ import formatCurrency from '~/libs/formatCurrency'; import { IProductDetail } from '~/types/product'; import { useProductDetail } from '../stores/useProductDetail'; import AddToCart from './AddToCart'; +import AddToQuotation from './AddToQuotation'; import Link from 'next/link'; import { getAuth } from '~/libs/auth'; @@ -110,6 +111,14 @@ const PriceAction = ({ product }: Props) => { /> )}
+
+ +
); }; -- cgit v1.2.3 From 94a96e371f554c7d87e2a6edbc6bdb7fe9228fd6 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 16 Oct 2024 13:19:45 +0700 Subject: add CR tambahin info jumlah barang ready stock yang bisa di pick up --- src-migrate/modules/cart/components/Item.tsx | 17 ++++++++++------- .../product-detail/components/PriceAction.tsx | 22 ++++++++++++++++++++-- src-migrate/types/cart.ts | 1 + src/lib/checkout/components/Checkout.jsx | 1 + src/lib/transaction/components/Transaction.jsx | 6 +++++- 5 files changed, 37 insertions(+), 10 deletions(-) diff --git a/src-migrate/modules/cart/components/Item.tsx b/src-migrate/modules/cart/components/Item.tsx index 6ffbb524..ab2e7ce1 100644 --- a/src-migrate/modules/cart/components/Item.tsx +++ b/src-migrate/modules/cart/components/Item.tsx @@ -36,26 +36,29 @@ const CartItem = ({ item, editable = true, selfPicking}: Props) => { )}
- Selamat! Pembelian anda lebih hemat {' '} + Selamat! Pembelian anda lebih hemat{' '} - Rp{formatCurrency((item.package_price || 0) * item.quantity - item.subtotal)} + Rp + {formatCurrency( + (item.package_price || 0) * item.quantity - item.subtotal + )}
)}
- {editable && ( - - )} + {editable && }
- {(item.is_in_bu) && (item.on_hand_qty >= item.quantity) && ( + {item?.available_quantity > 0 && (
- *Barang ini bisa di pickup maksimal pukul 16.00 + {item.quantity <= item?.available_quantity + ? '*Barang ini bisa di pickup maksimal pukul 16.00' + : `*${item?.available_quantity} Barang ini bisa di pickup maksimal pukul 16.00`}
)} diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 3544fa26..5171308c 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -2,11 +2,12 @@ import style from '../styles/price-action.module.css'; import Image from 'next/image'; import Link from 'next/link'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import formatCurrency from '~/libs/formatCurrency'; import { IProductDetail } from '~/types/product'; import { useProductDetail } from '../stores/useProductDetail'; import AddToCart from './AddToCart'; +import odooApi from '~/libs/odooApi'; type Props = { product: IProductDetail; @@ -25,7 +26,7 @@ const PriceAction = ({ product }: Props) => { selectedVariant, sla, } = useProductDetail(); - + const [qtyPickUp, setQtyPickUp] = useState(0); useEffect(() => { setActive(selectedVariant); if (product.variants.length > 2 && product.variants[0].price.price === 0) { @@ -39,6 +40,18 @@ const PriceAction = ({ product }: Props) => { } }, [product, setActive, selectedVariant]); + useEffect(() => { + const fetchData = async () => { + const qty_available = await odooApi( + 'GET', + `/api/v1/product_variant/${selectedVariant.id}/qty_available` + ); + + setQtyPickUp(qty_available?.qty); + }; + fetchData(); + }, [selectedVariant]); + let voucherPastiHemat = 0; if ( @@ -143,6 +156,11 @@ const PriceAction = ({ product }: Props) => { )}
+ {qtyPickUp > 0 && ( +
+ * {qtyPickUp} barang bisa di pickup +
+ )}
diff --git a/src-migrate/types/cart.ts b/src-migrate/types/cart.ts index a3115103..05fdcadb 100644 --- a/src-migrate/types/cart.ts +++ b/src-migrate/types/cart.ts @@ -34,6 +34,7 @@ export type CartItem = { stock: number; is_in_bu: boolean; on_hand_qty: number; + available_quantity: number; weight: number; attributes: string[]; parent: { diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 4c7e852f..e872b7f6 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -442,6 +442,7 @@ const Checkout = () => { const productOrder = products.map((product) => ({ product_id: product.id, quantity: product.quantity, + available_quantity: product?.availableQuantity, })); let data = { // partner_shipping_id: auth.partnerId, diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index 4d401037..d001c7f4 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -778,6 +778,10 @@ const Transaction = ({ id }) => { ? `| ${product?.attributes.join(', ')}` : ''}
+
+ {product.availableQuantity} barang ini bisa di + pickup maksimal pukul 16.00 +
{/* @@ -879,7 +883,7 @@ const Transaction = ({ id }) => {
- )} + )} {transaction?.data?.productsRejectLine.length > 0 && (
-- cgit v1.2.3 From 6d89fa79c9fdbb1961b15a463250f28e5c684b4a Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 16 Oct 2024 13:33:00 +0700 Subject: delete console log --- src/lib/product/components/Product/ProductDesktopVariant.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index 55effdfb..2e005eea 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -36,8 +36,6 @@ const ProductDesktopVariant = ({ const auth = useAuth(); const { slug } = router.query; - console.log('ini product variant', product); - const [lowestPrice, setLowestPrice] = useState(null); const [addCartAlert, setAddCartAlert] = useState(false); -- cgit v1.2.3 From fc77794a015f8fed5e8af4c9a320ca8b0fbb7d23 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 16 Oct 2024 13:42:39 +0700 Subject: update tampilan mobile --- src/lib/variant/components/VariantCard.jsx | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/src/lib/variant/components/VariantCard.jsx b/src/lib/variant/components/VariantCard.jsx index 68cdf54f..08b7a97e 100644 --- a/src/lib/variant/components/VariantCard.jsx +++ b/src/lib/variant/components/VariantCard.jsx @@ -103,30 +103,42 @@ const VariantCard = ({ product, openOnClick = true, buyMore = false }) => {
-
-

{product.parent.name}

+

+ {product.parent.name} +

{product.code || '-'} - {product.attributes.length > 0 ? ` ・ ${product.attributes.join(', ')}` : ''} + {product.attributes.length > 0 + ? ` ・ ${product.attributes.join(', ')}` + : ''}

Berat Item : {product?.weight} Kg x {product?.quantity} Barang

+

+ {product.availableQuantity} barang ini bisa di pickup maksimal pukul + 16.00 +

{product.hasFlashsale && ( <>

{currencyFormat(product.price.price)}

- {product.price.discountPercentage}% + + {product.price.discountPercentage}% + )}

{product.price.priceDiscount > 0 - ? currencyFormat(product.price.priceDiscount) + ' × ' + product.quantity + ' Barang' + ? currencyFormat(product.price.priceDiscount) + + ' × ' + + product.quantity + + ' Barang' : ''}

-- cgit v1.2.3 From f073b22e917acde22c21808906a37270e274085f Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 24 Oct 2024 10:04:38 +0700 Subject: ubah image --- public/images/writing.png | Bin 0 -> 7010 bytes .../modules/product-detail/components/AddToQuotation.tsx | 10 ++++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 public/images/writing.png diff --git a/public/images/writing.png b/public/images/writing.png new file mode 100644 index 00000000..17fedd74 Binary files /dev/null and b/public/images/writing.png differ diff --git a/src-migrate/modules/product-detail/components/AddToQuotation.tsx b/src-migrate/modules/product-detail/components/AddToQuotation.tsx index cc91f729..09a7501d 100644 --- a/src-migrate/modules/product-detail/components/AddToQuotation.tsx +++ b/src-migrate/modules/product-detail/components/AddToQuotation.tsx @@ -120,9 +120,15 @@ const AddToQuotation = ({ onClick={handleButton} color={'red'} colorScheme='white' - className='w-full border-2 flex items-center' + className='w-full border-2 p-2 hover:bg-slate-100 flex items-center' > - + Penawaran Harga Instan Date: Thu, 24 Oct 2024 10:12:11 +0700 Subject: avoid error code --- src-migrate/modules/product-detail/components/PriceAction.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index a3126cdd..a9b17f92 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -8,7 +8,6 @@ import { IProductDetail } from '~/types/product'; import { useProductDetail } from '../stores/useProductDetail'; import AddToCart from './AddToCart'; import AddToQuotation from './AddToQuotation'; -import Link from 'next/link'; import { getAuth } from '~/libs/auth'; type Props = { -- cgit v1.2.3 From 7350eb1edcfb7e9776fec70849ee0c64a795eda3 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 24 Oct 2024 11:35:14 +0700 Subject: add penawaran instan mobile --- .../product-detail/components/AddToQuotation.tsx | 8 +++--- .../components/Product/ProductDesktopVariant.jsx | 31 +++++++++++++++++++- .../components/Product/ProductMobileVariant.jsx | 33 ++++++++++++++++++++-- src/lib/quotation/components/Quotation.jsx | 5 +++- 4 files changed, 69 insertions(+), 8 deletions(-) diff --git a/src-migrate/modules/product-detail/components/AddToQuotation.tsx b/src-migrate/modules/product-detail/components/AddToQuotation.tsx index 09a7501d..f9b6c2b3 100644 --- a/src-migrate/modules/product-detail/components/AddToQuotation.tsx +++ b/src-migrate/modules/product-detail/components/AddToQuotation.tsx @@ -120,14 +120,14 @@ const AddToQuotation = ({ onClick={handleButton} color={'red'} colorScheme='white' - className='w-full border-2 p-2 hover:bg-slate-100 flex items-center' + className='w-full border-2 p-2 gap-1 hover:bg-slate-100 flex items-center' > Penawaran Harga Instan diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index 55effdfb..e04e0d16 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -17,7 +17,7 @@ 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 ImageNext from 'next/image'; import { RWebShare } from 'react-web-share'; import productSimilarApi from '../../api/productSimilarApi'; import ProductCard from '../ProductCard'; @@ -119,6 +119,20 @@ const ProductDesktopVariant = ({ router.push(`/shop/checkout?source=buy`); }; + const handleButton = (variant) => { + const quantity = quantityInput; + if (!validQuantity(quantity)) return; + + updateItemCart({ + productId: variant, + quantity, + programLineId: null, + selected: true, + source: 'buy', + }); + router.push('/shop/quotation?source=buy'); + }; + const variantSectionRef = useRef(null); const goToVariantSection = () => { if (variantSectionRef.current) { @@ -403,6 +417,21 @@ const ProductDesktopVariant = ({ Beli

+
+
diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx index cf0ad41f..5a2f63a5 100644 --- a/src/lib/quotation/components/Quotation.jsx +++ b/src/lib/quotation/components/Quotation.jsx @@ -39,9 +39,12 @@ const { getProductsCheckout } = require('@/lib/checkout/api/checkoutApi'); const Quotation = () => { const router = useRouter(); const auth = useAuth(); + const query = router.query.source ?? null; const { data: cartCheckout } = useQuery('cartCheckout', () => - getProductsCheckout() + getProductsCheckout({ + source: query, + }) ); const { setRefreshCart } = useProductCartContext(); -- cgit v1.2.3 From c64472c479ec282af9b606a76358922b25752be0 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 25 Oct 2024 15:03:53 +0700 Subject: update revisi CR --- .../product-detail/components/PriceAction.tsx | 20 ++++++++++++++++---- .../components/Product/ProductDesktopVariant.jsx | 8 +++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 5171308c..a87f8ae0 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -8,6 +8,7 @@ import { IProductDetail } from '~/types/product'; import { useProductDetail } from '../stores/useProductDetail'; import AddToCart from './AddToCart'; import odooApi from '~/libs/odooApi'; +import { Button, Skeleton } from '@chakra-ui/react'; type Props = { product: IProductDetail; @@ -52,6 +53,10 @@ const PriceAction = ({ product }: Props) => { fetchData(); }, [selectedVariant]); + useEffect(() => { + setQuantityInput('1'); + }, [selectedVariant]); + let voucherPastiHemat = 0; if ( @@ -137,10 +142,17 @@ const PriceAction = ({ product }: Props) => {
- - {' '} + Stock : {sla?.qty}{' '} - + + {/* + {' '} + */}
{product?.is_in_bu && ( @@ -157,7 +169,7 @@ const PriceAction = ({ product }: Props) => {
{qtyPickUp > 0 && ( -
+
* {qtyPickUp} barang bisa di pickup
)} diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index 2e005eea..32ce345e 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -364,14 +364,16 @@ const ProductDesktopVariant = ({
- - {' '} Stock : {product?.sla?.qty}{' '} - +
{product?.sla?.qty > 0 && ( -- cgit v1.2.3 From fdd6902a19ee050d97937252ef70bafa8fbe44d9 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 25 Oct 2024 16:19:56 +0700 Subject: update code --- .../modules/product-detail/components/Information.tsx | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 8655517d..b3311cb5 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -56,13 +56,15 @@ const Information = ({ product }: Props) => { }; useEffect(() => { - getsla(); - setInputValue( - selectedVariant?.code + - (selectedVariant?.attributes[0] - ? ' - ' + selectedVariant?.attributes[0] - : '') - ); + if (selectedVariant) { + getsla(); + setInputValue( + selectedVariant?.code + + (selectedVariant?.attributes[0] + ? ' - ' + selectedVariant?.attributes[0] + : '') + ); + } }, [selectedVariant]); const handleOnChange = (vals: any) => { -- cgit v1.2.3 From 4a5391def4c5d2e8991643287d40e3c3b53980be Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Tue, 5 Nov 2024 15:49:45 +0700 Subject: add redis --- package.json | 1 + src/components/ui/HeroBanner.jsx | 37 ++-- src/lib/home/components/BannerSection.jsx | 38 +++-- src/lib/home/components/CategoryDynamic.jsx | 247 +++++++++++---------------- src/lib/home/components/PromotionProgram.jsx | 96 ++++++----- src/pages/api/banner-section.js | 44 +++++ src/pages/api/category-management.js | 85 +++++++++ src/pages/api/hero-banner.js | 45 +++++ src/pages/api/shop/brands.js | 42 +++-- 9 files changed, 407 insertions(+), 228 deletions(-) create mode 100644 src/pages/api/banner-section.js create mode 100644 src/pages/api/category-management.js create mode 100644 src/pages/api/hero-banner.js diff --git a/package.json b/package.json index a846749c..4f594f0c 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "react-query": "^3.39.3", "react-select": "^5.8.0", "react-web-share": "^2.0.2", + "redis": "^4.7.0", "snakecase-keys": "^5.5.0", "swiper": "^8.4.4", "tw-merge": "^0.0.1-alpha.3", diff --git a/src/components/ui/HeroBanner.jsx b/src/components/ui/HeroBanner.jsx index 64838b85..2eea5915 100644 --- a/src/components/ui/HeroBanner.jsx +++ b/src/components/ui/HeroBanner.jsx @@ -6,7 +6,7 @@ import 'swiper/css/pagination'; import { Swiper, SwiperSlide } from 'swiper/react'; import Image from 'next/image'; -import { useMemo } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; import { bannerApi } from '@/api/bannerApi'; @@ -27,7 +27,20 @@ const swiperBanner = { }; const HeroBanner = () => { - const heroBanner = useQuery('heroBanner', bannerApi({ type: 'index-a-1' })); + // const heroBanner = useQuery('heroBanner', bannerApi({ type: 'index-a-1' })); + const [data, setData] = useState(null); + useEffect(() => { + const fetchData = async () => { + const res = await fetch(`/api/hero-banner?type=index-a-1`); + const { data } = await res.json(); + if (data) { + setData(data); + } + }; + + fetchData(); + }, []); + const heroBanner = data; const swiperBannerMobile = { ...swiperBanner, @@ -44,9 +57,9 @@ const HeroBanner = () => { }; const BannerComponent = useMemo(() => { - if (!heroBanner.data) return null; + if (!heroBanner) return null; - return heroBanner.data.map((banner, index) => ( + return heroBanner.map((banner, index) => ( { width={1152} height={768} className='w-full h-auto' - priority={index === 0} - loading={index === 0 ? 'eager' : 'lazy'} - placeholder="blur" - blurDataURL="/images/indoteknik-placeholder.png" - sizes="(max-width: 768px) 100vw, 50vw" + priority={index === 0} + loading={index === 0 ? 'eager' : 'lazy'} + placeholder='blur' + blurDataURL='/images/indoteknik-placeholder.png' + sizes='(max-width: 768px) 100vw, 50vw' /> )); - }, [heroBanner.data]); + }, [heroBanner]); return ( <> 0} + isLoaded={heroBanner?.length > 0} height='68vw' duration='750ms' delay='100ms' @@ -81,7 +94,7 @@ const HeroBanner = () => { - {heroBanner.data?.length > 0 && ( + {heroBanner?.length > 0 && ( {BannerComponent} )} diff --git a/src/lib/home/components/BannerSection.jsx b/src/lib/home/components/BannerSection.jsx index 60d38f8f..303b5c4b 100644 --- a/src/lib/home/components/BannerSection.jsx +++ b/src/lib/home/components/BannerSection.jsx @@ -11,27 +11,33 @@ const BannerSection = () => { const [shouldFetch, setShouldFetch] = useState(false); useEffect(() => { - const localData = localStorage.getItem('Homepage_bannerSection'); - if (localData) { - setData(JSON.parse(localData)); - }else{ - setShouldFetch(true); - } - }, []); - - // const fetchBannerSection = async () => await bannerSectionApi(); - const getBannerSection = useQuery('bannerSection', bannerApi({ type: 'home-banner' }), { - enabled: shouldFetch, - onSuccess: (data) => { + const fetchCategoryData = async () => { + const res = await fetch('/api/banner-section'); + const { data } = await res.json(); if (data) { - localStorage.setItem('Homepage_bannerSection', JSON.stringify(data)); setData(data); } - }, - }); + }; - const bannerSection = data; + fetchCategoryData(); + }, []); + // const fetchBannerSection = async () => await bannerSectionApi(); + const getBannerSection = useQuery( + 'bannerSection', + bannerApi({ type: 'home-banner' }), + { + enabled: shouldFetch, + onSuccess: (data) => { + if (data) { + localStorage.setItem('Homepage_bannerSection', JSON.stringify(data)); + setData(data); + } + }, + } + ); + + const bannerSection = data; return ( bannerSection && bannerSection?.length > 0 && ( diff --git a/src/lib/home/components/CategoryDynamic.jsx b/src/lib/home/components/CategoryDynamic.jsx index e62575f7..cc4f42b7 100644 --- a/src/lib/home/components/CategoryDynamic.jsx +++ b/src/lib/home/components/CategoryDynamic.jsx @@ -1,81 +1,33 @@ -import React, { useEffect, useState, useCallback } from 'react'; -import { - fetchCategoryManagementSolr, - fetchCategoryManagementVersion, -} from '../api/categoryManagementApi'; +import React, { useEffect, useState } from 'react'; +import { fetchCategoryManagementSolr } from '../api/categoryManagementApi'; +import { Skeleton } from '@chakra-ui/react'; import NextImage from 'next/image'; import Link from 'next/link'; import { createSlug } from '@/core/utils/slug'; -import { Skeleton } from '@chakra-ui/react'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; import 'swiper/css/navigation'; import 'swiper/css/pagination'; import { Pagination } from 'swiper'; -const saveToLocalStorage = (key, data, version) => { - const now = new Date(); - const item = { - value: data, - version: version, - lastFetchedTime: now.getTime(), - }; - localStorage.setItem(key, JSON.stringify(item)); -}; - -const getFromLocalStorage = (key) => { - const itemStr = localStorage.getItem(key); - if (!itemStr) return null; - - const item = JSON.parse(itemStr); - return item; -}; - -const getElapsedTime = (lastFetchedTime) => { - const now = new Date(); - return now.getTime() - lastFetchedTime; -}; - const CategoryDynamic = () => { const [categoryManagement, setCategoryManagement] = useState([]); - const [isLoading, setIsLoading] = useState(false); - - const loadBrand = useCallback(async () => { - const cachedData = getFromLocalStorage('homepage_categoryDynamic'); - - if (cachedData) { - // Hitung selisih waktu antara saat ini dengan waktu terakhir data di-fetch - const elapsedTime = getElapsedTime(cachedData.lastFetchedTime); - - if (elapsedTime < 24 * 60 * 60 * 1000) { - setCategoryManagement(cachedData.value); - return; - } - } + const [isLoading, setIsLoading] = useState(true); - const latestVersion = await fetchCategoryManagementVersion(); - if (cachedData && cachedData.version === latestVersion) { - // perbarui waktu - saveToLocalStorage( - 'homepage_categoryDynamic', - cachedData.value, - latestVersion - ); - setCategoryManagement(cachedData.value); - } else { + useEffect(() => { + const fetchCategoryData = async () => { setIsLoading(true); - const items = await fetchCategoryManagementSolr(); + const res = await fetch('/api/category-management'); + const { data } = await res.json(); + if (data) { + setCategoryManagement(data); + } setIsLoading(false); + }; - saveToLocalStorage('homepage_categoryDynamic', items, latestVersion); - setCategoryManagement(items); - } + fetchCategoryData(); }, []); - useEffect(() => { - loadBrand(); - }, [loadBrand]); - const swiperBanner = { modules: [Pagination], classNames: 'mySwiper', @@ -90,104 +42,99 @@ const CategoryDynamic = () => { return (
{categoryManagement && - categoryManagement?.map((category) => { - return ( - -
-
-

- {category.name} -

- - Lihat Semua - -
+ categoryManagement.map((category) => ( + +
+
+

+ {category.name} +

+ + Lihat Semua + +
- {/* Swiper for SubCategories */} - - {category.categories.map((subCategory) => { - return ( - -
-
-
- -
-

- {subCategory?.name} -

+ + {category?.categories?.map((subCategory) => ( + +
+
+
+ +
+

+ {subCategory?.name} +

+ + Lihat Semua + +
+
+
+ {subCategory.child_frontend_id_i.map( + (childCategory) => ( +
- Lihat Semua + +
+

+ {childCategory.name} +

+
-
-
- {subCategory.child_frontend_id_i.map( - (childCategory) => ( -
- - -
-

- {childCategory.name} -

-
- -
- ) - )} -
-
+ ) + )}
-
- ); - })} -
-
- - ); - })} +
+
+ + ))} + +
+ + ))}
); }; diff --git a/src/lib/home/components/PromotionProgram.jsx b/src/lib/home/components/PromotionProgram.jsx index 7433e7f0..562fa138 100644 --- a/src/lib/home/components/PromotionProgram.jsx +++ b/src/lib/home/components/PromotionProgram.jsx @@ -10,32 +10,50 @@ const BannerSection = () => { const { isMobile, isDesktop } = useDevice(); const [data, setData] = useState(null); const [shouldFetch, setShouldFetch] = useState(false); - useEffect(() => { - const localData = localStorage.getItem('Homepage_promotionProgram'); - if (localData) { - setData(JSON.parse(localData)); - }else{ - setShouldFetch(true); - } - },[]) - - const getPromotionProgram = useQuery( - 'promotionProgram', - bannerApi({ type: 'banner-promotion' }),{ - enabled: shouldFetch, - onSuccess: (data) => { - if (data) { - localStorage.setItem('Homepage_promotionProgram', JSON.stringify(data)); - setData(data); - } + const fetchData = async () => { + const res = await fetch(`/api/hero-banner?type=banner-promotion`); + const { data } = await res.json(); + if (data) { + setData(data); } - } - ); + }; + + fetchData(); + }, []); + + // useEffect(() => { + // const localData = localStorage.getItem('Homepage_promotionProgram'); + // if (localData) { + // setData(JSON.parse(localData)); + // } else { + // setShouldFetch(true); + // } + // }, []); - const promotionProgram = data + // const getPromotionProgram = useQuery( + // 'promotionProgram', + // bannerApi({ type: 'banner-promotion' }), + // { + // enabled: shouldFetch, + // onSuccess: (data) => { + // if (data) { + // localStorage.setItem( + // 'Homepage_promotionProgram', + // JSON.stringify(data) + // ); + // setData(data); + // } + // }, + // } + // ); - if (getPromotionProgram?.isLoading && !data) { + const promotionProgram = data; + + // if (getPromotionProgram?.isLoading && !data) { + // return ; + // } + if (!data) { return ; } @@ -62,24 +80,22 @@ const BannerSection = () => { )}
- {isDesktop && - promotionProgram && - promotionProgram?.length > 0 && ( -
- {promotionProgram?.map((banner) => ( - - {banner.name} - - ))} -
- )} + {isDesktop && promotionProgram && promotionProgram?.length > 0 && ( +
+ {promotionProgram?.map((banner) => ( + + {banner.name} + + ))} +
+ )} {isMobile && ( diff --git a/src/pages/api/banner-section.js b/src/pages/api/banner-section.js new file mode 100644 index 00000000..7d7040c0 --- /dev/null +++ b/src/pages/api/banner-section.js @@ -0,0 +1,44 @@ +import odooApi from '@/core/api/odooApi'; +import { createClient } from 'redis'; + +const client = createClient(); + +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; + +export default async function handler(req, res) { + try { + await connectRedis(); + const cacheKey = 'hero-banner'; + // await client.del(cacheKey); + let cachedData = await client.get(cacheKey); + + if (cachedData) { + const data = JSON.parse(cachedData); + return res.status(200).json({ data }); + } else { + const dataBannerSections = await odooApi( + 'GET', + '/api/v1/banner?type=home-banner' + ); + + // Simpan hasil fetch ke Redis dengan masa kadaluarsa 3 hari (259200 detik) + await client.set( + cacheKey, + JSON.stringify(dataBannerSections), + 'EX', + 259200 + ); + + return res.status(200).json({ data: dataBannerSections }); + } + } catch (error) { + console.error('Error interacting with Redis or fetching data:', error); + return res.status(500).json({ error: 'Internal Server Error' }); + } +} diff --git a/src/pages/api/category-management.js b/src/pages/api/category-management.js new file mode 100644 index 00000000..f05d8644 --- /dev/null +++ b/src/pages/api/category-management.js @@ -0,0 +1,85 @@ +import { createClient } from 'redis'; +// import { fetchCategoryManagementSolr } from '../../lib/home/api/categoryManagementApi'; +const client = createClient(); +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; + +export default async function handler(req, res) { + try { + await connectRedis(); + // await client.del('homepage_categoryDynamic'); + + let cachedData; + if (req.method === 'GET') { + cachedData = await client.get('homepage_categoryDynamic'); + + if (!cachedData) { + const items = await fetchCategoryManagementSolr(); + await client.set( + 'homepage_categoryDynamic', + JSON.stringify(items), + 'EX', + 259200 // Expiry 3 hari + ); + cachedData = await client.get('homepage_categoryDynamic'); + } + const data = cachedData ? JSON.parse(cachedData) : null; + res.status(200).json({ data }); + } else { + res.setHeader('Allow', ['GET']); + res.status(405).end(`Method ${req.method} Not Allowed`); + } + } catch (error) { + console.error('Error interacting with Redis:', error); + res.status(500).json({ error: 'Error interacting with Redis' }); + } +} + +const fetchCategoryManagementSolr = async () => { + let sort = 'sort=sequence_i asc'; + try { + const response = await fetch( + `http://34.101.189.218:8983/solr/category_management/query?q=*:*&q.op=OR&indent=true&${sort}&&rows=20` + ); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + const data = await response.json(); + const promotions = await map(data.response.docs); + return promotions; + } catch (error) { + console.error('Error fetching promotion data:', error); + return []; + } +}; +const map = async (promotions) => { + return promotions.map((promotion) => { + let parsedCategories = promotion.categories.map((category) => { + // Parse string JSON utama + let parsedCategory = JSON.parse(category); + + // Parse setiap elemen di child_frontend_id_i jika ada + if (parsedCategory.child_frontend_id_i) { + parsedCategory.child_frontend_id_i = + parsedCategory.child_frontend_id_i.map((child) => JSON.parse(child)); + } + + return parsedCategory; + }); + let productMapped = { + id: promotion.id, + name: promotion.name_s, + image: promotion.image_s, + sequence: promotion.sequence_i, + numFound: promotion.numFound_i, + categories: parsedCategories, + category_id: promotion.category_id_i, + }; + return productMapped; + }); +}; diff --git a/src/pages/api/hero-banner.js b/src/pages/api/hero-banner.js new file mode 100644 index 00000000..b57f86e0 --- /dev/null +++ b/src/pages/api/hero-banner.js @@ -0,0 +1,45 @@ +import odooApi from '@/core/api/odooApi'; +import { createClient } from 'redis'; + +const client = createClient(); + +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; + +export default async function handler(req, res) { + const { type } = req.query; + try { + await connectRedis(); + const cacheKey = `homepage_bannerSection_${type}`; + // await client.del(cacheKey); + let cachedData = await client.get(cacheKey); + + if (cachedData) { + const data = JSON.parse(cachedData); + return res.status(200).json({ data }); + } else { + const dataBannerSections = await odooApi( + 'GET', + `/api/v1/banner?type=${type}` + ); + + // Simpan hasil fetch ke Redis dengan masa kadaluarsa 3 hari (259200 detik) + await client.set( + cacheKey, + JSON.stringify(dataBannerSections), + 'EX', + 259200 + ); + + return res.status(200).json({ data: dataBannerSections }); + } + } catch (error) { + console.error('Error interacting with Redis or fetching data:', error); + return res.status(500).json({ error: 'Internal Server Error' }); + } +} diff --git a/src/pages/api/shop/brands.js b/src/pages/api/shop/brands.js index 9c2824b3..380b3369 100644 --- a/src/pages/api/shop/brands.js +++ b/src/pages/api/shop/brands.js @@ -1,8 +1,20 @@ import axios from 'axios'; +import { createClient } from 'redis'; const SOLR_HOST = process.env.SOLR_HOST; +const client = createClient(); + +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; export default async function handler(req, res) { + await connectRedis(); + try { let params = '*:*'; let sort = @@ -11,12 +23,12 @@ export default async function handler(req, res) { if (req.query.params) { rows = 100; - switch (req?.query?.params) { + switch (req.query.params) { case 'level_s': params = 'level_s:prioritas'; break; case 'search': - params = `name_s:"${req?.query?.q.toLowerCase()}"`; + params = `name_s:"${req.query.q.toLowerCase()}"`; sort = ''; rows = 1; break; @@ -24,11 +36,23 @@ export default async function handler(req, res) { params = `name_s:${req.query.params}`.toLowerCase(); } } - if(req.query.rows) rows = req.query.rows; - + if (req.query.rows) rows = req.query.rows; + + const cacheKey = `brands_home`; + let cachedData = await client.get(cacheKey); + + if (cachedData) { + console.log('Retrieving data from Redis cache'); + return res.status(200).json(JSON.parse(cachedData)); + } + + // Fetch data from Solr const url = `${SOLR_HOST}/solr/brands/select?q=${params}&q.op=OR&indent=true&rows=${rows}&${sort}`; - let brands = await axios(url); - let dataBrands = responseMap(brands.data.response.docs); + const brands = await axios(url); + const dataBrands = responseMap(brands.data.response.docs); + + // Store fetched data in Redis with 3-day expiration + await client.set(cacheKey, JSON.stringify(dataBrands), 'EX', 259200); res.status(200).json(dataBrands); } catch (error) { @@ -39,13 +63,11 @@ export default async function handler(req, res) { const responseMap = (brands) => { return brands.map((brand) => { - let brandMapping = { + return { id: brand.id, name: brand.display_name_s, logo: brand.image_s || '', - sequance: brand.sequence_i || '', + sequence: brand.sequence_i || '', }; - - return brandMapping; }); }; -- cgit v1.2.3 From f7a32dbccbdefd478ed6bd6190bd4fa395eda720 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 6 Nov 2024 15:42:41 +0700 Subject: update mobile view --- src-migrate/modules/product-detail/components/Information.tsx | 2 +- src-migrate/modules/product-detail/components/PriceAction.tsx | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 8655517d..590e8afd 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -189,7 +189,7 @@ const Information = ({ product }: Props) => { width={100} src={product.manufacture.logo} alt={product.manufacture.name} - className='h-8 object-cover' + className='h-8 object-fit' /> ) : ( diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index 3544fa26..dc384dda 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -7,6 +7,7 @@ import formatCurrency from '~/libs/formatCurrency'; import { IProductDetail } from '~/types/product'; import { useProductDetail } from '../stores/useProductDetail'; import AddToCart from './AddToCart'; +import useDevice from '@/core/hooks/useDevice'; type Props = { product: IProductDetail; @@ -25,7 +26,7 @@ const PriceAction = ({ product }: Props) => { selectedVariant, sla, } = useProductDetail(); - + const { isDesktop, isMobile } = useDevice(); useEffect(() => { setActive(selectedVariant); if (product.variants.length > 2 && product.variants[0].price.price === 0) { @@ -53,7 +54,10 @@ const PriceAction = ({ product }: Props) => { return (
{!!activePrice && activePrice.price > 0 && ( -- cgit v1.2.3 From 337e0f377624e5a0b46aa0ed3c3c16ba4e289965 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 6 Nov 2024 16:36:57 +0700 Subject: update mobile view --- src-migrate/modules/product-detail/components/PriceAction.tsx | 2 +- src/core/components/layouts/BasicLayout.jsx | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src-migrate/modules/product-detail/components/PriceAction.tsx b/src-migrate/modules/product-detail/components/PriceAction.tsx index a44e35ec..b549d39f 100644 --- a/src-migrate/modules/product-detail/components/PriceAction.tsx +++ b/src-migrate/modules/product-detail/components/PriceAction.tsx @@ -58,7 +58,7 @@ const PriceAction = ({ product }: Props) => {
diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index d6b96f36..1b62bf05 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -24,6 +24,7 @@ const BasicLayout = ({ children }) => { const [highlight, setHighlight] = useState(false); const [buttonPosition, setButtonPosition] = useState(null); const [wobble, setWobble] = useState(false); + const [isProductPage, setIsProductPage] = useState(false); const { isDesktop, isMobile } = useDevice(); @@ -46,6 +47,9 @@ const BasicLayout = ({ children }) => { setUrlPath(router.asPath); } + if (router.pathname.includes('/shop/product/')) { + setIsProductPage(true); + } }, [product, router]); useEffect(() => { @@ -111,7 +115,11 @@ const BasicLayout = ({ children }) => { {children} -
+
Date: Thu, 7 Nov 2024 08:35:30 +0700 Subject: update mobile type --- .../modules/product-detail/components/Image.tsx | 116 ++++++++++----------- src-migrate/types/product.ts | 1 + src/utils/solrMapping.js | 1 + 3 files changed, 60 insertions(+), 58 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index 30ca0d34..29710df8 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -1,22 +1,22 @@ import style from '../styles/image.module.css'; import ImageNext from 'next/image'; -import React, { useEffect, useMemo, useState } from 'react' -import { InfoIcon } from 'lucide-react' -import { Tooltip } from '@chakra-ui/react' +import React, { useEffect, useMemo, useState } from 'react'; +import { InfoIcon } from 'lucide-react'; +import { Tooltip } from '@chakra-ui/react'; -import { IProductDetail } from '~/types/product' -import ImageUI from '~/components/ui/image' +import { IProductDetail } from '~/types/product'; +import ImageUI from '~/components/ui/image'; import moment from 'moment'; - +import useDevice from '@/core/hooks/useDevice'; type Props = { - product: IProductDetail -} + product: IProductDetail; +}; const Image = ({ product }: Props) => { - const flashSale = product.flash_sale + const flashSale = product.flash_sale; const [count, setCount] = useState(flashSale?.remaining_time || 0); - + const { isDesktop, isMobile } = useDevice(); useEffect(() => { let interval: NodeJS.Timeout; @@ -34,59 +34,58 @@ const Image = ({ product }: Props) => { }; }, [flashSale?.remaining_time]); - const duration = moment.duration(count, 'seconds') - + const duration = moment.duration(count, 'seconds'); + console.log('product', product); const image = useMemo(() => { - if (product.image) return product.image + '?ratio=square' - return '/images/noimage.jpeg' - }, [product.image]) + if (isMobile && product.image_mobile) { + return product.image_mobile + '?ratio=square'; + } + }, [product.image, product.image_mobile]); return (
{/*
*/} - -
-
- {product.isSni && ( - - )} -
-
- {product.isTkdn && ( - - )} -
-
- {/*
*/} - - + +
+
+ {product.isSni && ( + + )} +
+
+ {product.isTkdn && ( + + )} +
+
+ {/*
*/}
-
+
@@ -94,7 +93,7 @@ const Image = ({ product }: Props) => { {flashSale.remaining_time > 0 && (
-
+
{
-
{Math.floor(product.lowest_price.discount_percentage)}%
+
+ {Math.floor(product.lowest_price.discount_percentage)}% +
{ {duration.seconds().toString().padStart(2, '0')}
-
)}
- ) -} + ); +}; -export default Image \ No newline at end of file +export default Image; diff --git a/src-migrate/types/product.ts b/src-migrate/types/product.ts index 31ea0ce1..e43a5ebb 100644 --- a/src-migrate/types/product.ts +++ b/src-migrate/types/product.ts @@ -3,6 +3,7 @@ import { IProductVariantDetail } from './productVariant'; export interface IProduct { id: number; image: string; + image_mobile: string; code: string; display_name: string; name: string; diff --git a/src/utils/solrMapping.js b/src/utils/solrMapping.js index f73e966a..d4b3f08c 100644 --- a/src/utils/solrMapping.js +++ b/src/utils/solrMapping.js @@ -43,6 +43,7 @@ export const productMappingSolr = (products, pricelist) => { let productMapped = { id: product.product_id_i || '', image: product.image_s || '', + imageMobile: product.image_mobile_s || '', code: product.default_code_s || '', description: product.description_t || '', displayName: product.display_name_s || '', -- cgit v1.2.3 From 982a18543beefdd33f20c294b06b7a1a87103b2f Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 7 Nov 2024 09:16:51 +0700 Subject: update mobile image --- .../product-card/components/ProductCard.tsx | 176 +++++++++++---------- .../modules/product-detail/components/Image.tsx | 6 +- src/lib/product/components/ProductCard.jsx | 13 +- 3 files changed, 105 insertions(+), 90 deletions(-) diff --git a/src-migrate/modules/product-card/components/ProductCard.tsx b/src-migrate/modules/product-card/components/ProductCard.tsx index 0febfadb..a439cdc8 100644 --- a/src-migrate/modules/product-card/components/ProductCard.tsx +++ b/src-migrate/modules/product-card/components/ProductCard.tsx @@ -1,95 +1,108 @@ -import style from '../styles/product-card.module.css' +import style from '../styles/product-card.module.css'; import ImageNext from 'next/image'; -import clsx from 'clsx' -import Link from 'next/link' -import React, { useEffect, useMemo, useState } 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' -import { createSlug } from '~/libs/slug' -import { IProduct } from '~/types/product' - +import clsx from 'clsx'; +import Link from 'next/link'; +import React, { useEffect, useMemo, useState } 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'; +import { createSlug } from '~/libs/slug'; +import { IProduct } from '~/types/product'; +import useDevice from '@/core/hooks/useDevice'; type Props = { - product: IProduct - layout?: 'vertical' | 'horizontal' -} + product: IProduct; + layout?: 'vertical' | 'horizontal'; +}; const ProductCard = ({ product, layout = 'vertical' }: Props) => { - const utmSource = useUtmSource() - + const utmSource = useUtmSource(); + const { isDesktop, isMobile } = useDevice(); const URL = { - product: createSlug('/shop/product/', product.name, product.id.toString()) + `?utm_source=${utmSource}`, - manufacture: createSlug('/shop/brands/', product.manufacture.name, product.manufacture.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() + ), + }; const image = useMemo(() => { - if (product.image) return product.image + '?ratio=square' - return '/images/noimage.jpeg' - }, [product.image]) + if (!isDesktop && product.image_mobile) { + return product.image_mobile + '?ratio=square'; + } else { + if (product.image) return product.image + '?ratio=square'; + return '/images/noimage.jpeg'; + } + }, [product.image, product.image_mobile]); return ( -
-
+
- -
- {product.name} -
-
- {product.isSni && ( - - )} -
-
- {product.isTkdn && ( - - )} +
+ {product.name} +
+
+ {product.isSni && ( + + )} +
+
+ {product.isTkdn && ( + + )} +
-
{product.variant_total > 1 && ( -
{product.variant_total} Varian
+
+ {product.variant_total} Varian +
)}
-
- +
+ {product.manufacture.name} @@ -113,17 +126,15 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => {
- Inc PPN: - Rp {formatCurrency(Math.round(product.lowest_price.price * 1.11))} + Inc PPN: Rp{' '} + {formatCurrency(Math.round(product.lowest_price.price * 1.11))}
{product.stock_total > 0 && ( -
- Ready Stock -
+
Ready Stock
)} {product.qty_sold > 0 && (
@@ -131,14 +142,11 @@ const ProductCard = ({ product, layout = 'vertical' }: Props) => {
)}
-
- ) -} - -const classPrefix = ({ layout }: Props) => { + ); +}; -} +const classPrefix = ({ layout }: Props) => {}; -export default ProductCard \ No newline at end of file +export default ProductCard; diff --git a/src-migrate/modules/product-detail/components/Image.tsx b/src-migrate/modules/product-detail/components/Image.tsx index 29710df8..96ae2027 100644 --- a/src-migrate/modules/product-detail/components/Image.tsx +++ b/src-migrate/modules/product-detail/components/Image.tsx @@ -35,10 +35,12 @@ const Image = ({ product }: Props) => { }, [flashSale?.remaining_time]); const duration = moment.duration(count, 'seconds'); - console.log('product', product); const image = useMemo(() => { - if (isMobile && product.image_mobile) { + if (!isDesktop && product.image_mobile) { return product.image_mobile + '?ratio=square'; + } else { + if (product.image) return product.image + '?ratio=square'; + return '/images/noimage.jpeg'; } }, [product.image, product.image_mobile]); diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index d3b50302..a480bbdd 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -10,12 +10,13 @@ import { sellingProductFormat } from '@/core/utils/formatValue'; import { createSlug } from '@/core/utils/slug'; import whatsappUrl from '@/core/utils/whatsappUrl'; import useUtmSource from '~/hooks/useUtmSource'; +import useDevice from '@/core/hooks/useDevice'; const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { const router = useRouter(); const utmSource = useUtmSource(); const [discount, setDiscount] = useState(0); - + const { isDesktop, isMobile } = useDevice(); let voucherPastiHemat = 0; voucherPastiHemat = product?.newVoucherPastiHemat[0]; @@ -26,9 +27,13 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { }); const image = useMemo(() => { - if (product.image) return product.image + '?ratio=square'; - return '/images/noimage.jpeg'; - }, [product.image]); + if (!isDesktop && product.image_mobile) { + return product.image_mobile + '?ratio=square'; + } else { + if (product.image) return product.image + '?ratio=square'; + return '/images/noimage.jpeg'; + } + }, [product.image, product.image_mobile]); const URL = { product: -- cgit v1.2.3 From ccc224e8a6261e40e2ea1db08e21a49d280fd1c4 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 7 Nov 2024 15:23:42 +0700 Subject: update fetch data logic --- src/pages/api/shop/variant-detail.js | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/pages/api/shop/variant-detail.js b/src/pages/api/shop/variant-detail.js index 08ce75b8..af3525b3 100644 --- a/src/pages/api/shop/variant-detail.js +++ b/src/pages/api/shop/variant-detail.js @@ -1,21 +1,28 @@ -import { productMappingSolr, variantsMappingSolr } from '@/utils/solrMapping' -import axios from 'axios' +import { productMappingSolr, variantsMappingSolr } from '@/utils/solrMapping'; +import axios from 'axios'; export default async function handler(req, res) { try { let productVariants = await axios( process.env.SOLR_HOST + `/solr/variants/select?q=id:${req.query.id}&q.op=OR&indent=true` - ) - let auth = req.query.auth === 'false' ? JSON.parse(req.query.auth) : req.query.auth + ); + let template_id = productVariants.data.response.docs[0].template_id_i; + let auth = + req.query.auth === 'false' ? JSON.parse(req.query.auth) : req.query.auth; let productTemplate = await axios( - process.env.SOLR_HOST + `/solr/product/select?q=id:${req.query.id}&q.op=OR&indent=true` - ) - let result = variantsMappingSolr(productTemplate.data.response.docs, productVariants.data.response.docs, auth || false) - - res.status(200).json(result) + process.env.SOLR_HOST + + `/solr/product/select?q=id:${template_id}&q.op=OR&indent=true` + ); + let result = variantsMappingSolr( + productTemplate.data.response.docs, + productVariants.data.response.docs, + auth || false + ); + + res.status(200).json(result); } catch (error) { - console.error('Error fetching data from Solr:', error) - res.status(500).json({ error: 'Internal Server Error' }) + console.error('Error fetching data from Solr:', error); + res.status(500).json({ error: 'Internal Server Error' }); } -} \ No newline at end of file +} -- cgit v1.2.3 From 0ecb7ba546cd1fdd3811f76aa09b20642ab4952c Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 11 Nov 2024 11:21:47 +0700 Subject: update redis category management mobile --- src/lib/home/components/CategoryDynamicMobile.jsx | 40 +++++------------------ 1 file changed, 9 insertions(+), 31 deletions(-) diff --git a/src/lib/home/components/CategoryDynamicMobile.jsx b/src/lib/home/components/CategoryDynamicMobile.jsx index 55654b0e..6ede1147 100644 --- a/src/lib/home/components/CategoryDynamicMobile.jsx +++ b/src/lib/home/components/CategoryDynamicMobile.jsx @@ -37,42 +37,20 @@ const CategoryDynamicMobile = () => { const [categoryManagement, setCategoryManagement] = useState([]); const [isLoading, setIsLoading] = useState(false); - const loadCategoryManagement = useCallback(async () => { - const cachedData = getFromLocalStorage('homepage_categoryDynamic'); - - if (cachedData) { - // Hitung selisih waktu antara saat ini dengan waktu terakhir data di-fetch - const elapsedTime = getElapsedTime(cachedData.lastFetchedTime); - - if (elapsedTime < 24 * 60 * 60 * 1000) { - setCategoryManagement(cachedData.value); - return; - } - } - - const latestVersion = await fetchCategoryManagementVersion(); - if (cachedData && cachedData.version === latestVersion) { - // perbarui waktu - saveToLocalStorage( - 'homepage_categoryDynamic', - cachedData.value, - latestVersion - ); - setCategoryManagement(cachedData.value); - } else { + useEffect(() => { + const fetchCategoryData = async () => { setIsLoading(true); - const items = await fetchCategoryManagementSolr(); + const res = await fetch('/api/category-management'); + const { data } = await res.json(); + if (data) { + setCategoryManagement(data); + } setIsLoading(false); + }; - saveToLocalStorage('homepage_categoryDynamic', items, latestVersion); - setCategoryManagement(items); - } + fetchCategoryData(); }, []); - useEffect(() => { - loadCategoryManagement(); - }, [loadCategoryManagement]); - useEffect(() => { if (categoryManagement?.length > 0) { const initialSelections = categoryManagement.reduce((acc, category) => { -- cgit v1.2.3 From 2e1c0fb77932bdd0681f8d7c9cfd292a9ac52b3b Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 11 Nov 2024 14:11:57 +0700 Subject: update undifined select category --- src-migrate/modules/product-detail/components/Information.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index 590e8afd..3c2eb537 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -66,7 +66,7 @@ const Information = ({ product }: Props) => { }, [selectedVariant]); const handleOnChange = (vals: any) => { - let code = vals.split(' ')[0]; + let code = vals.replace(/\s-\s.*$/, '').trim(); let variant = variantOptions.find((item) => item.code === code); setSelectedVariant(variant); setInputValue( -- cgit v1.2.3 From 0d3c0cf6a00ef81bfdb944490e48f16af41fc029 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 13 Nov 2024 10:08:16 +0700 Subject: add radis --- src-migrate/modules/page-content/index.tsx | 33 +++++------ src/components/ui/HeroBannerSecondary.jsx | 69 +++++++++++++++-------- src/core/components/elements/Navbar/TopBanner.jsx | 53 ++++++++++------- src/lib/home/components/CategoryDynamicMobile.jsx | 23 -------- src/pages/api/hero-banner.js | 4 +- src/pages/api/page-content.js | 44 +++++++++++++++ src/pages/api/shop/brands.js | 3 - 7 files changed, 136 insertions(+), 93 deletions(-) create mode 100644 src/pages/api/page-content.js diff --git a/src-migrate/modules/page-content/index.tsx b/src-migrate/modules/page-content/index.tsx index 3423ca8b..54ee0a04 100644 --- a/src-migrate/modules/page-content/index.tsx +++ b/src-migrate/modules/page-content/index.tsx @@ -10,28 +10,21 @@ type Props = { const PageContent = ({ path }: Props) => { const [localData, setData] = useState(); const [shouldFetch, setShouldFetch] = useState(false); + const [isLoading, setIsLoading] = useState(false); useEffect(() => { - const localData = localStorage.getItem(`page-content:${path}`); - if (localData) { - setData(JSON.parse(localData)); - }else{ - setShouldFetch(true); - } - },[]) - - const { data, isLoading } = useQuery( - `page-content:${path}`, - async () => await getPageContent({ path }), { - enabled: shouldFetch, - onSuccess: (data) => { - if (data) { - localStorage.setItem(`page-content:${path}`, JSON.stringify(data)); - setData(data); - } - }, - } - ); + const fetchData = async () => { + setIsLoading(true); + const res = await fetch(`/api/page-content?path=${path}`); + const { data } = await res.json(); + if (data) { + setData(data); + } + setIsLoading(false); + }; + + fetchData(); + }, []); const parsedContent = useMemo(() => { if (!localData) return ''; diff --git a/src/components/ui/HeroBannerSecondary.jsx b/src/components/ui/HeroBannerSecondary.jsx index a7b32a4a..6074c9a6 100644 --- a/src/components/ui/HeroBannerSecondary.jsx +++ b/src/components/ui/HeroBannerSecondary.jsx @@ -1,39 +1,58 @@ -import Link from '@/core/components/elements/Link/Link' -import { getRandomInt } from '@/utils/getRandomInt' -import Image from 'next/image' -import { useMemo } from 'react' -import { useQuery } from 'react-query' -import { HeroBannerSkeleton } from '../skeleton/BannerSkeleton' -import { bannerApi } from '@/api/bannerApi' +import Link from '@/core/components/elements/Link/Link'; +import { getRandomInt } from '@/utils/getRandomInt'; +import Image from 'next/image'; +import { useMemo, useEffect, useState } from 'react'; +import { useQuery } from 'react-query'; +import { HeroBannerSkeleton } from '../skeleton/BannerSkeleton'; +import { bannerApi } from '@/api/bannerApi'; const HeroBannerSecondary = () => { - const heroBannerSecondary = useQuery('heroBannerSecondary', bannerApi({ type: 'index-a-2' })) + const [heroBannerSecondary, setHeroBannerSecondary] = useState([]); + const [isLoading, setIsLoading] = useState(false); + // const heroBannerSecondary = useQuery( + // 'heroBannerSecondary', + // bannerApi({ type: 'index-a-2' }) + // ); - const randomIndex = useMemo(() => { - if (!heroBannerSecondary.data) return null - const length = heroBannerSecondary.data?.length - return getRandomInt(length) - }, [heroBannerSecondary.data]) + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + const res = await fetch(`/api/hero-banner?type=index-a-2`); + const { data } = await res.json(); + if (data) { + setHeroBannerSecondary(data); + } + setIsLoading(false); + }; + + fetchData(); + }, []); - if (heroBannerSecondary.isLoading) return + const randomIndex = useMemo(() => { + if (!heroBannerSecondary) return null; + const length = heroBannerSecondary?.length; + return getRandomInt(length); + }, [heroBannerSecondary]); + if (isLoading) return ; return ( - heroBannerSecondary.data && randomIndex !== null && ( - + heroBannerSecondary && + randomIndex !== null && ( + {heroBannerSecondary.data[randomIndex].name} ) ); -} +}; -export default HeroBannerSecondary +export default HeroBannerSecondary; diff --git a/src/core/components/elements/Navbar/TopBanner.jsx b/src/core/components/elements/Navbar/TopBanner.jsx index f438ae67..709495ce 100644 --- a/src/core/components/elements/Navbar/TopBanner.jsx +++ b/src/core/components/elements/Navbar/TopBanner.jsx @@ -1,22 +1,37 @@ import Image from 'next/image'; -import { useQuery } from 'react-query';import useDevice from '@/core/hooks/useDevice' +import { useQuery } from 'react-query'; +import useDevice from '@/core/hooks/useDevice'; import odooApi from '@/core/api/odooApi'; import SmoothRender from '~/components/ui/smooth-render'; import Link from '../Link/Link'; import { background } from '@chakra-ui/react'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; const TopBanner = ({ onLoad = () => {} }) => { - const { isDesktop, isMobile } = useDevice() - const topBanner = useQuery({ - queryKey: 'topBanner', - queryFn: async () => await odooApi('GET', '/api/v1/banner?type=top-banner'), - refetchOnWindowFocus: false, - }); + const [topBanner, setTopBanner] = useState([]); + const { isDesktop, isMobile } = useDevice(); + + useEffect(() => { + const fetchData = async () => { + const res = await fetch(`/api/hero-banner?type=top-banner`); + const { data } = await res.json(); + if (data) { + setTopBanner(data); + } + }; + + fetchData(); + }, []); + + // const topBanner = useQuery({ + // queryKey: 'topBanner', + // queryFn: async () => await odooApi('GET', '/api/v1/banner?type=top-banner'), + // refetchOnWindowFocus: false, + // }); // const backgroundColor = topBanner.data?.[0]?.backgroundColor || 'transparent'; - const hasData = topBanner.data?.length > 0; - const data = topBanner.data?.[0] || null; + const hasData = topBanner?.length > 0; + const data = topBanner?.[0] || null; useEffect(() => { if (hasData) { @@ -31,17 +46,15 @@ const TopBanner = ({ onLoad = () => {} }) => { duration='700ms' delay='300ms' className='h-auto' - > + > - - - + href={data?.url} + className='block bg-cover bg-center h-3 md:h-6 lg:h-[36px]' + style={{ + backgroundImage: `url('${data?.image}')`, + }} + > + ); }; diff --git a/src/lib/home/components/CategoryDynamicMobile.jsx b/src/lib/home/components/CategoryDynamicMobile.jsx index 6ede1147..67ae6f5f 100644 --- a/src/lib/home/components/CategoryDynamicMobile.jsx +++ b/src/lib/home/components/CategoryDynamicMobile.jsx @@ -9,29 +9,6 @@ import { fetchCategoryManagementVersion, } from '../api/categoryManagementApi'; -const saveToLocalStorage = (key, data, version) => { - const now = new Date(); - const item = { - value: data, - version: version, - lastFetchedTime: now.getTime(), - }; - localStorage.setItem(key, JSON.stringify(item)); -}; - -const getFromLocalStorage = (key) => { - const itemStr = localStorage.getItem(key); - if (!itemStr) return null; - - const item = JSON.parse(itemStr); - return item; -}; - -const getElapsedTime = (lastFetchedTime) => { - const now = new Date(); - return now.getTime() - lastFetchedTime; -}; - const CategoryDynamicMobile = () => { const [selectedCategory, setSelectedCategory] = useState({}); const [categoryManagement, setCategoryManagement] = useState([]); diff --git a/src/pages/api/hero-banner.js b/src/pages/api/hero-banner.js index b57f86e0..7a348cfa 100644 --- a/src/pages/api/hero-banner.js +++ b/src/pages/api/hero-banner.js @@ -35,8 +35,8 @@ export default async function handler(req, res) { 'EX', 259200 ); - - return res.status(200).json({ data: dataBannerSections }); + cachedData = await client.get(cacheKey); + return res.status(200).json({ data: cachedData }); } } catch (error) { console.error('Error interacting with Redis or fetching data:', error); diff --git a/src/pages/api/page-content.js b/src/pages/api/page-content.js new file mode 100644 index 00000000..3cb8a237 --- /dev/null +++ b/src/pages/api/page-content.js @@ -0,0 +1,44 @@ +import { createClient } from 'redis'; +import { getPageContent } from '~/services/pageContent'; +// import { fetchCategoryManagementSolr } from '../../lib/home/api/categoryManagementApi'; +const client = createClient(); +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; + +export default async function handler(req, res) { + const { path } = req.query; + try { + await connectRedis(); + // await client.del('onbording-popup'); + + let cachedData; + if (req.method === 'GET') { + cachedData = await client.get(`page-content:${path}`); + + if (!cachedData) { + const items = await getPageContent({ path }); + console.log('items', items); + await client.set( + `page-content:${path}`, + JSON.stringify(items), + 'EX', + 604800 // Expiry 1 minggu + ); + cachedData = await client.get(`page-content:${path}`); + } + const data = cachedData ? JSON.parse(cachedData) : null; + res.status(200).json({ data }); + } else { + res.setHeader('Allow', ['GET']); + res.status(405).end(`Method ${req.method} Not Allowed`); + } + } catch (error) { + console.error('Error interacting with Redis:', error); + res.status(500).json({ error: 'Error interacting with Redis' }); + } +} diff --git a/src/pages/api/shop/brands.js b/src/pages/api/shop/brands.js index 380b3369..219f2cb0 100644 --- a/src/pages/api/shop/brands.js +++ b/src/pages/api/shop/brands.js @@ -42,16 +42,13 @@ export default async function handler(req, res) { let cachedData = await client.get(cacheKey); if (cachedData) { - console.log('Retrieving data from Redis cache'); return res.status(200).json(JSON.parse(cachedData)); } - // Fetch data from Solr const url = `${SOLR_HOST}/solr/brands/select?q=${params}&q.op=OR&indent=true&rows=${rows}&${sort}`; const brands = await axios(url); const dataBrands = responseMap(brands.data.response.docs); - // Store fetched data in Redis with 3-day expiration await client.set(cacheKey, JSON.stringify(dataBrands), 'EX', 259200); res.status(200).json(dataBrands); -- cgit v1.2.3 From 4368e38cce90e401a0ba141f2b20d23db1886cf8 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 13 Nov 2024 11:05:25 +0700 Subject: fix bug ao shipment pagination --- .../components/Product/ProductDesktopVariant.jsx | 1 - src/lib/shipment/components/Shipments.jsx | 204 +++++++++++++-------- 2 files changed, 128 insertions(+), 77 deletions(-) diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index cca8ec5e..f4569574 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -231,7 +231,6 @@ const ProductDesktopVariant = ({ }; fetchData(); }, [product]); - console.log('product', product); return ( diff --git a/src/lib/shipment/components/Shipments.jsx b/src/lib/shipment/components/Shipments.jsx index 115bbd3a..20dbb013 100644 --- a/src/lib/shipment/components/Shipments.jsx +++ b/src/lib/shipment/components/Shipments.jsx @@ -1,62 +1,83 @@ -import DesktopView from '@/core/components/views/DesktopView' -import MobileView from '@/core/components/views/MobileView' -import Menu from '@/lib/auth/components/Menu' -import { EllipsisVerticalIcon, MagnifyingGlassIcon } from '@heroicons/react/24/outline' -import ImageNext from 'next/image' -import { useRouter } from 'next/router' -import { useQuery } from 'react-query' -import _, { forEach } from 'lodash-contrib' -import Spinner from '@/core/components/elements/Spinner/Spinner' -import Manifest from '@/lib/treckingAwb/component/Manifest' -import { useState } from 'react' -import Pagination from '@/core/components/elements/Pagination/Pagination' -import Link from 'next/link' -import TransactionStatusBadge from '@/lib/transaction/components/TransactionStatusBadge' +import DesktopView from '@/core/components/views/DesktopView'; +import MobileView from '@/core/components/views/MobileView'; +import Menu from '@/lib/auth/components/Menu'; +import { + EllipsisVerticalIcon, + MagnifyingGlassIcon, +} from '@heroicons/react/24/outline'; +import ImageNext from 'next/image'; +import { useEffect } from 'react'; +import { useRouter } from 'next/router'; +import { useQuery } from 'react-query'; +import _, { forEach } from 'lodash-contrib'; +import Spinner from '@/core/components/elements/Spinner/Spinner'; +import Manifest from '@/lib/treckingAwb/component/Manifest'; +import { useState } from 'react'; +import Pagination from '@/core/components/elements/Pagination/Pagination'; +import Link from 'next/link'; +import TransactionStatusBadge from '@/lib/transaction/components/TransactionStatusBadge'; -const { listShipments } = require('../api/listShipment') +const { listShipments } = require('../api/listShipment'); const Shipments = () => { - const router = useRouter() - const { q = '', page = 1 } = router.query - const [paramStatus, setParamStatus] = useState(null) - - const limit = 15 + const router = useRouter(); + const { q = '', page = 1, status = null } = router.query; + const [paramStatus, setParamStatus] = useState(status); + const limit = 15; const query = { q: q, status: paramStatus, offset: (page - 1) * limit, - limit - } - const [inputQuery, setInputQuery] = useState(q) - const queryString = _.toQuery(query) + limit, + }; + const [inputQuery, setInputQuery] = useState(q); + const queryString = _.toQuery(query); const { data: shipments } = useQuery('shipments' + queryString, () => listShipments({ query: queryString }) - ) - const [idAWB, setIdAWB] = useState(null) + ); + const [idAWB, setIdAWB] = useState(null); - const pageCount = Math.ceil(shipments?.pickingTotal / limit) - let pageQuery = _.omit(query, ['limit', 'offset', 'context']) - pageQuery = _.pickBy(pageQuery, _.identity) - pageQuery = _.toQuery(pageQuery) + const pageCount = Math.ceil(shipments?.pickingTotal / limit); + let pageQuery = _.omit(query, ['limit', 'offset', 'context']); + pageQuery = _.pickBy(pageQuery, _.identity); + pageQuery = _.toQuery(pageQuery); const closePopup = () => { - setIdAWB(null) - } + setIdAWB(null); + }; const handleSubmit = async (e) => { - e.preventDefault() - router.push(`${router.pathname}?q=${inputQuery}`) - } + e.preventDefault(); + router.push(`${router.pathname}?q=${inputQuery}`); + }; const filterStatus = async (status) => { if (status === paramStatus) { - setParamStatus(null) + setParamStatus(null); } else { - setParamStatus(status) + setParamStatus(status); } - } + }; + + useEffect(() => { + const resetQuery = () => { + const newQuery = { + status: paramStatus || undefined, + q: '', + page: 1, + }; + router.push({ + pathname: router.pathname, + query: newQuery, + }); + }; + + if (paramStatus !== status) { + resetQuery(); + } + }, [paramStatus]); return ( <> @@ -84,7 +105,10 @@ const Shipments = () => { {shipments?.pickings.map((shipment) => ( -
+

@@ -93,7 +117,9 @@ const Shipments = () => { {shipment.carrierName || '-'}

-

No. Resi : {shipment.trackingNumber || '-'}

+

+ No. Resi : {shipment.trackingNumber || '-'} +

{shipment?.status === 'completed' && ( @@ -116,11 +142,17 @@ const Shipments = () => {
- No. Transaksi + + No. Transaksi + -

{shipment.saleOrder.name}

+

+ {shipment.saleOrder.name} +

- {shipment.date} + + {shipment.date} +
@@ -176,7 +212,8 @@ const Shipments = () => {
- Lacak pengiriman untuk setiap transaksi anda semakin mudah di Indoteknik.com + Lacak pengiriman untuk setiap transaksi anda semakin mudah di + Indoteknik.com
@@ -190,7 +227,9 @@ const Shipments = () => {
-

Detail Pengiriman

+

+ Detail Pengiriman +

{ value={inputQuery} onChange={(e) => setInputQuery(e.target.value)} /> -
@@ -254,7 +296,7 @@ const Shipments = () => {
@@ -263,16 +305,16 @@ const Shipments = () => { - ) -} + ); +}; const CardStatus = ({ device, paramStatus, shipments, filterStatus }) => { - const status = [`pending`, `shipment`, `completed`] + const status = [`pending`, `shipment`, `completed`]; return ( <> {status.map((value) => { - const statusData = getStatusLabel(device, value, shipments) + const statusData = getStatusLabel(device, value, shipments); if (device === 'desktop') { return (
{ }`} onClick={() => filterStatus(value)} > -

{statusData.label}

+

+ {statusData.label} +

{statusData.image}

{statusData.shipCount} Pesanan

- ) + ); } else { return (
{ {statusData.shipCount} {'>'}
- ) + ); } })} - ) -} + ); +}; const getStatusLabel = (device, status, shipments) => { - let images = null + let images = null; switch (status) { case 'pending': if (device === 'desktop') { @@ -328,40 +372,48 @@ const getStatusLabel = (device, status, shipments) => { />
- ) + ); } else { images = (
- ) + ); } return { label: 'Pending', shipCount: shipments?.summary?.pendingCount, - image: images - } + image: images, + }; case 'shipment': if (device === 'desktop') { images = (
- +
- ) + ); } else { images = (
- +
- ) + ); } return { label: 'Pengiriman', shipCount: shipments?.summary?.shipmentCount, - image: images - } + image: images, + }; case 'completed': if (device === 'desktop') { images = ( @@ -375,22 +427,22 @@ const getStatusLabel = (device, status, shipments) => { />
- ) + ); } else { images = (
- ) + ); } return { label: 'Pesanan Tiba', shipCount: shipments?.summary?.completedCount, - image: images - } + image: images, + }; default: - return 'Status Tidak Dikenal' + return 'Status Tidak Dikenal'; } -} +}; -export default Shipments +export default Shipments; -- cgit v1.2.3 From baa029e404f499f7e09b9d9a152bd8c676411684 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 13 Nov 2024 15:23:59 +0700 Subject: fix bug select variant --- .../modules/product-detail/components/Information.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index ec606423..d500ff6e 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -12,7 +12,7 @@ import { useEffect, useRef, useState } from 'react'; import currencyFormat from '@/core/utils/currencyFormat'; import { InputGroup, InputRightElement } from '@chakra-ui/react'; -import { ChevronDownIcon } from '@heroicons/react/24/outline'; +import { ChevronDownIcon, XMarkIcon } from '@heroicons/react/24/outline'; import Image from 'next/image'; import { formatToShortText } from '~/libs/formatNumber'; import { createSlug } from '~/libs/slug'; @@ -35,7 +35,7 @@ const Information = ({ product }: Props) => { const [inputValue, setInputValue] = useState( selectedVariant?.code + ' - ' + selectedVariant?.attributes[0] ); - + const [disableFilter, setDisableFilter] = useState(false); const inputRef = useRef(null); const [variantOptions, setVariantOptions] = useState( @@ -85,6 +85,7 @@ const Information = ({ product }: Props) => { }; const handleOnKeyUp = (e: any) => { + setDisableFilter(false); setInputValue(e.target.value); }; @@ -98,6 +99,7 @@ const Information = ({ product }: Props) => { {' '} handleOnChange(vals)} @@ -108,7 +110,11 @@ const Information = ({ product }: Props) => { value={inputValue as string} onChange={(e) => handleOnKeyUp(e)} /> - + + setDisableFilter(true)} + /> inputRef?.current?.focus()} -- cgit v1.2.3 From c18b82d1cc4342cc99f384da9b1236b25815e2d7 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Wed, 13 Nov 2024 15:30:23 +0700 Subject: fix bug --- src-migrate/modules/product-detail/components/Information.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src-migrate/modules/product-detail/components/Information.tsx b/src-migrate/modules/product-detail/components/Information.tsx index d500ff6e..b9f4be91 100644 --- a/src-migrate/modules/product-detail/components/Information.tsx +++ b/src-migrate/modules/product-detail/components/Information.tsx @@ -12,7 +12,7 @@ import { useEffect, useRef, useState } from 'react'; import currencyFormat from '@/core/utils/currencyFormat'; import { InputGroup, InputRightElement } from '@chakra-ui/react'; -import { ChevronDownIcon, XMarkIcon } from '@heroicons/react/24/outline'; +import { ChevronDownIcon } from '@heroicons/react/24/outline'; import Image from 'next/image'; import { formatToShortText } from '~/libs/formatNumber'; import { createSlug } from '~/libs/slug'; @@ -68,6 +68,7 @@ const Information = ({ product }: Props) => { }, [selectedVariant]); const handleOnChange = (vals: any) => { + setDisableFilter(true); let code = vals.replace(/\s-\s.*$/, '').trim(); let variant = variantOptions.find((item) => item.code === code); setSelectedVariant(variant); @@ -109,12 +110,9 @@ const Information = ({ product }: Props) => { ref={inputRef} value={inputValue as string} onChange={(e) => handleOnKeyUp(e)} + onFocus={() => setDisableFilter(true)} /> - setDisableFilter(true)} - /> inputRef?.current?.focus()} -- cgit v1.2.3 From 1c37bf46eadd0ba26713933b65d9f1c2fdd7b33f Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 14 Nov 2024 09:51:08 +0700 Subject: comnent alt --- src/core/components/elements/Navbar/NavbarDesktop.jsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index 04cf76d1..fa3df5bf 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -391,7 +391,7 @@ const SocialMedias = () => ( > @@ -403,7 +403,7 @@ const SocialMedias = () => ( > @@ -423,7 +423,7 @@ const SocialMedias = () => ( > @@ -435,7 +435,7 @@ const SocialMedias = () => ( > @@ -447,7 +447,7 @@ const SocialMedias = () => ( > @@ -459,7 +459,7 @@ const SocialMedias = () => ( > -- cgit v1.2.3 From 6e7aa296bae3e290785da7ad941c521a685be1e9 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 14 Nov 2024 10:58:15 +0700 Subject: add google tag ads --- src/lib/checkout/components/Checkout.jsx | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index e83e719c..afbf1e6c 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -413,7 +413,12 @@ const Checkout = () => { Math.round(parseInt(finalShippingAmt * 1.1) / 1000) * 1000; const finalGT = GT < 0 ? 0 : GT; setGrandTotal(finalGT); - }, [biayaKirim, cartCheckout?.grandTotal, activeVoucher, activeVoucherShipping]); + }, [ + biayaKirim, + cartCheckout?.grandTotal, + activeVoucher, + activeVoucherShipping, + ]); const checkout = async () => { const file = poFile.current.files[0]; @@ -484,6 +489,11 @@ const Checkout = () => { transaction_id: isCheckouted.id, }); + gtag('set', 'user_data', { + email: auth.email, + phone_number: auth.mobile ?? auth.phone, + }); + for (const product of products) deleteItemCart({ productId: product.id }); if (grandTotal > 0) { const payment = await axios.post( @@ -501,7 +511,7 @@ const Checkout = () => { } } - /* const midtrans = async () => { + /* const midtrans = async () => { for (const product of products) deleteItemCart({ productId: product.id }); if (grandTotal > 0) { const payment = await axios.post( @@ -1193,7 +1203,11 @@ const Checkout = () => {
Biaya Kirim

{etdFix}

-
{currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000)}
+
+ {currencyFormat( + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} +
{activeVoucherShipping && voucherShippingAmt && (
@@ -1494,7 +1508,11 @@ const Checkout = () => { Biaya Kirim

{etdFix}

-
{currencyFormat(Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000) }
+
+ {currencyFormat( + Math.round(parseInt(biayaKirim * 1.1) / 1000) * 1000 + )} +
{activeVoucherShipping && voucherShippingAmt && (
-- cgit v1.2.3 From 90eb9e8ea858be3b38a6481050a029a32980f417 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 14 Nov 2024 11:57:01 +0700 Subject: update bug no flash sale --- src/pages/api/shop/search.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js index ace281f7..1f0ff504 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -89,7 +89,8 @@ export default async function handler(req, res) { ]; if (fq && source != 'similar') { - filterQueries.push(fq); + // filterQueries.push(fq); + fq.push(...filterQueries); } const fq_ = filterQueries.join(' AND '); -- cgit v1.2.3 From ff9a983d8b179308075bc7bd8833d1901c30486d Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Thu, 14 Nov 2024 14:20:48 +0700 Subject: change code name brand to truncate --- src/lib/product/components/ProductCard.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index d3b50302..936d4ee0 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -143,7 +143,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => {
{product?.manufacture?.name ? ( - + {product.manufacture.name} ) : ( -- cgit v1.2.3 From 36871796910127a2bbda3cedab00a150a24375e4 Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Thu, 14 Nov 2024 15:47:03 +0700 Subject: CR - text WA --- src/core/utils/whatsappUrl.js | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/core/utils/whatsappUrl.js b/src/core/utils/whatsappUrl.js index 7a129aa6..c840e105 100644 --- a/src/core/utils/whatsappUrl.js +++ b/src/core/utils/whatsappUrl.js @@ -2,28 +2,31 @@ import { getAuth } from "./auth" const whatsappUrl = (template = 'default', payload, urlPath = null) => { let user = getAuth() - if(!user){ - if(urlPath) return `/login?next=${urlPath}` - if(!urlPath) return '/login' - } + // if(!user){ + // if(urlPath) return `/login?next=${urlPath}` + // if(!urlPath) return '/login' + // } let parentName = user.parentName || '-' let url = 'https://wa.me/6281717181922' let text = 'Hallo Indoteknik.com,' + if(user){ + text += `Saya ${user.name}, Saya dari ${parentName}` + } switch (template) { case 'product': - text += ` Saya ${user.name} , Saya dari ${parentName} Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}` + text += ` Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}` break case 'productWeight': - text += ` Saya ${user.name} , Saya dari ${parentName} Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}` + text += ` Saya mencari barang dibawah ini\n\n: Brand = ${payload?.manufacture}\n\n Item Name = ${payload?.name}\n\nLink : ${payload?.url}` break case 'productSearch': - text += `Saya lagi cari-cari produk ${payload?.name}, bisa bantu saya cari produknya?` + text += ` Saya lagi cari-cari produk ${payload?.name}, bisa bantu saya cari produknya?` break case null: - text += `Saya ${user.name}, Saya dari ${parentName} Bisa tolong bantu kebutuhan saya?` + text += ` Bisa tolong bantu kebutuhan saya?` break; default: - text += `Saya ${user.name}, Saya dari ${parentName} Bisa tolong bantu kebutuhan saya?` + text += ` Bisa tolong bantu kebutuhan saya?` break } if (text) url += `?text=${encodeURI(text)}` -- cgit v1.2.3 From 31fca1b5d023abb96aad53ac2e26b77ce16e51e1 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 15 Nov 2024 09:23:29 +0700 Subject: update error code --- src/lib/product/components/Product/ProductDesktopVariant.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index f4569574..6fe75fff 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -434,7 +434,7 @@ const ProductDesktopVariant = ({
Date: Fri, 15 Nov 2024 09:52:30 +0700 Subject: if has image brand then show brand image else brand name --- .../product/components/Product/ProductDesktopVariant.jsx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/lib/product/components/Product/ProductDesktopVariant.jsx b/src/lib/product/components/Product/ProductDesktopVariant.jsx index 6fe75fff..5dfd452b 100644 --- a/src/lib/product/components/Product/ProductDesktopVariant.jsx +++ b/src/lib/product/components/Product/ProductDesktopVariant.jsx @@ -264,11 +264,17 @@ const ProductDesktopVariant = ({ product.manufacture.id.toString() )} > - {product.manufacture.name} + {product?.manufacture.logo ? ( + {product.manufacture.name} + ) : ( +

+ {product.manufacture.name} +

+ )}
-- cgit v1.2.3 From c69b71c16ff7cc7a347394710d069ef711db08bb Mon Sep 17 00:00:00 2001 From: trisusilo48 Date: Fri, 15 Nov 2024 14:55:36 +0700 Subject: cr logic generate keywords --- src/pages/api/shop/search.js | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/pages/api/shop/search.js b/src/pages/api/shop/search.js index 1f0ff504..a789b469 100644 --- a/src/pages/api/shop/search.js +++ b/src/pages/api/shop/search.js @@ -73,8 +73,9 @@ export default async function handler(req, res) { const formattedQuery = `(${newQ .split(' ') - .map((term) => `${term}*`) + .map((term) => (term.length < 2 ? term : `${term}*`)) // Tambahkan '*' hanya jika panjang kata >= 2 .join(' ')})`; + const mm = checkQ.length > 2 ? checkQ.length > 5 @@ -94,6 +95,17 @@ export default async function handler(req, res) { } const fq_ = filterQueries.join(' AND '); + let keywords = newQ; + if (source === 'similar' || checkQ.length < 3) { + if (checkQ.length < 2 || checkQ[1].length < 2) { + keywords = newQ ; + } else { + keywords = newQ + '*'; + } + } else { + keywords = formattedQuery; + } + let offset = (page - 1) * limit; let parameter = [ 'facet.field=manufacture_name_s', @@ -102,13 +114,7 @@ export default async function handler(req, res) { 'indent=true', `facet.query=${escapeSolrQuery(q)}`, `q.op=OR`, - `q=${ - source == 'similar' || checkQ.length < 3 - ? checkQ.length < 2 - ? newQ - : newQ + '*' - : formattedQuery - }`, + `q=${keywords}`, `defType=edismax`, 'qf=name_s description_clean_t category_name manufacture_name_s variants_code_t variants_name_t category_id_ids default_code_s manufacture_id_i category_id_i ', `start=${parseInt(offset)}`, -- cgit v1.2.3 From d50e637383087cf03d2ad94ad6d076f30f3490c5 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 15 Nov 2024 15:36:38 +0700 Subject: delete cached --- src/pages/api/shop/brands.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/pages/api/shop/brands.js b/src/pages/api/shop/brands.js index 219f2cb0..d56e4b13 100644 --- a/src/pages/api/shop/brands.js +++ b/src/pages/api/shop/brands.js @@ -38,19 +38,10 @@ export default async function handler(req, res) { } if (req.query.rows) rows = req.query.rows; - const cacheKey = `brands_home`; - let cachedData = await client.get(cacheKey); - - if (cachedData) { - return res.status(200).json(JSON.parse(cachedData)); - } - const url = `${SOLR_HOST}/solr/brands/select?q=${params}&q.op=OR&indent=true&rows=${rows}&${sort}`; const brands = await axios(url); const dataBrands = responseMap(brands.data.response.docs); - await client.set(cacheKey, JSON.stringify(dataBrands), 'EX', 259200); - res.status(200).json(dataBrands); } catch (error) { console.error('Error fetching data from Solr:', error); -- cgit v1.2.3 From 48441ffd9a92827ffedf5e0b0f4e490132e02c82 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 15 Nov 2024 15:59:52 +0700 Subject: update radis error code preferredBrand --- src/lib/home/components/PreferredBrand.jsx | 82 ++++++++++++++++-------------- src/pages/api/shop/preferredBrand.js | 46 +++++++++++++++++ 2 files changed, 90 insertions(+), 38 deletions(-) create mode 100644 src/pages/api/shop/preferredBrand.js diff --git a/src/lib/home/components/PreferredBrand.jsx b/src/lib/home/components/PreferredBrand.jsx index eefced60..5a6a0570 100644 --- a/src/lib/home/components/PreferredBrand.jsx +++ b/src/lib/home/components/PreferredBrand.jsx @@ -1,49 +1,51 @@ -import { Swiper, SwiperSlide } from 'swiper/react' -import { Navigation, Pagination, Autoplay } from 'swiper'; -import { useCallback, useEffect, useState } from 'react' -import usePreferredBrand from '../hooks/usePreferredBrand' -import PreferredBrandSkeleton from './Skeleton/PreferredBrandSkeleton' -import BrandCard from '@/lib/brand/components/BrandCard' -import useDevice from '@/core/hooks/useDevice' -import Link from '@/core/components/elements/Link/Link' -import axios from 'axios' +import { Swiper, SwiperSlide } from 'swiper/react'; +import { Navigation, Pagination, Autoplay } from 'swiper'; +import { useCallback, useEffect, useState } from 'react'; +import usePreferredBrand from '../hooks/usePreferredBrand'; +import PreferredBrandSkeleton from './Skeleton/PreferredBrandSkeleton'; +import BrandCard from '@/lib/brand/components/BrandCard'; +import useDevice from '@/core/hooks/useDevice'; +import Link from '@/core/components/elements/Link/Link'; +import axios from 'axios'; const PreferredBrand = () => { - let query = '' - let params = 'prioritas' - const [isLoading, setIsLoading] = useState(true) - const [startWith, setStartWith] = useState(null) - const [manufactures, setManufactures] = useState([]) + let query = ''; + let params = 'prioritas'; + const [isLoading, setIsLoading] = useState(true); + const [startWith, setStartWith] = useState(null); + const [manufactures, setManufactures] = useState([]); const loadBrand = useCallback(async () => { - setIsLoading(true) - const name = startWith ? `${startWith}*` : '' - const result = await axios(`${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/brands?rows=20`) - - setIsLoading(false) - setManufactures((manufactures) => [...result.data]) - }, [startWith]) + setIsLoading(true); + const name = startWith ? `${startWith}*` : ''; + const result = await axios( + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/preferredBrand?rows=20` + ); + + setIsLoading(false); + setManufactures((manufactures) => [...result.data]); + }, [startWith]); const toggleStartWith = (alphabet) => { - setManufactures([]) + setManufactures([]); if (alphabet == startWith) { - setStartWith(null) - return + setStartWith(null); + return; } - setStartWith(alphabet) - } + setStartWith(alphabet); + }; useEffect(() => { - loadBrand() - }, []) + loadBrand(); + }, []); // const { preferredBrands } = usePreferredBrand(query) - const { isMobile, isDesktop } = useDevice() + const { isMobile, isDesktop } = useDevice(); const swiperBanner = { - modules:[Navigation, Pagination, Autoplay], + modules: [Navigation, Pagination, Autoplay], autoplay: { delay: 4000, - disableOnInteraction: false + disableOnInteraction: false, }, loop: true, className: 'h-[70px] md:h-[100px] w-full', @@ -53,13 +55,17 @@ const PreferredBrand = () => { dynamicBullets: true, dynamicMainBullets: isMobile ? 6 : 8, clickable: true, - } - } - const preferredBrandsData = manufactures ? manufactures.slice(0, 20) : [] + }, + }; + const preferredBrandsData = manufactures ? manufactures.slice(0, 20) : []; return (
-

Brand Pilihan

+

+ + Brand Pilihan + +

{isDesktop && ( Lihat Semua @@ -79,7 +85,7 @@ const PreferredBrand = () => { )}
- ) -} + ); +}; -export default PreferredBrand \ No newline at end of file +export default PreferredBrand; diff --git a/src/pages/api/shop/preferredBrand.js b/src/pages/api/shop/preferredBrand.js new file mode 100644 index 00000000..9480c63b --- /dev/null +++ b/src/pages/api/shop/preferredBrand.js @@ -0,0 +1,46 @@ +import odooApi from '@/core/api/odooApi'; +import { createClient } from 'redis'; + +const client = createClient(); + +client.on('error', (err) => console.error('Redis Client Error', err)); + +const connectRedis = async () => { + if (!client.isOpen) { + await client.connect(); + } +}; + +export default async function handler(req, res) { + try { + await connectRedis(); + // await client.del('preferredBrand'); + + let cachedData; + if (req.method === 'GET') { + cachedData = await client.get('preferredBrand'); + + if (!cachedData) { + const items = await odooApi( + 'GET', + '/api/v1/manufacture?level=prioritas' + ); + await client.set( + 'preferredBrand', + JSON.stringify(items), + 'EX', + 259200 // Expiry 3 hari + ); + cachedData = await client.get('preferredBrand'); + } + const data = cachedData ? JSON.parse(cachedData) : null; + res.status(200).json({ data }); + } else { + res.setHeader('Allow', ['GET']); + res.status(405).end(`Method ${req.method} Not Allowed`); + } + } catch (error) { + console.error('Error interacting with Redis:', error); + res.status(500).json({ error: 'Error interacting with Redis' }); + } +} -- cgit v1.2.3 From 9e967a55c575d24b740620325838ab857e7eef04 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 15 Nov 2024 16:04:05 +0700 Subject: back to code --- src/lib/home/components/PreferredBrand.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/home/components/PreferredBrand.jsx b/src/lib/home/components/PreferredBrand.jsx index 5a6a0570..9fb1228a 100644 --- a/src/lib/home/components/PreferredBrand.jsx +++ b/src/lib/home/components/PreferredBrand.jsx @@ -19,7 +19,7 @@ const PreferredBrand = () => { setIsLoading(true); const name = startWith ? `${startWith}*` : ''; const result = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/preferredBrand?rows=20` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/brands?rows=20` ); setIsLoading(false); -- cgit v1.2.3 From 607ddd5050b5ee900606984b60e74d094fbe89f9 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Fri, 15 Nov 2024 16:17:13 +0700 Subject: update fix code --- src/lib/home/components/PreferredBrand.jsx | 3 +- src/pages/api/shop/preferredBrand.js | 69 ++++++++++++++++++------------ 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/lib/home/components/PreferredBrand.jsx b/src/lib/home/components/PreferredBrand.jsx index 9fb1228a..b7a30503 100644 --- a/src/lib/home/components/PreferredBrand.jsx +++ b/src/lib/home/components/PreferredBrand.jsx @@ -19,9 +19,8 @@ const PreferredBrand = () => { setIsLoading(true); const name = startWith ? `${startWith}*` : ''; const result = await axios( - `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/brands?rows=20` + `${process.env.NEXT_PUBLIC_SELF_HOST}/api/shop/preferredBrand?rows=20` ); - setIsLoading(false); setManufactures((manufactures) => [...result.data]); }, [startWith]); diff --git a/src/pages/api/shop/preferredBrand.js b/src/pages/api/shop/preferredBrand.js index 9480c63b..4cb35c84 100644 --- a/src/pages/api/shop/preferredBrand.js +++ b/src/pages/api/shop/preferredBrand.js @@ -1,6 +1,7 @@ -import odooApi from '@/core/api/odooApi'; +import axios from 'axios'; import { createClient } from 'redis'; +const SOLR_HOST = process.env.SOLR_HOST; const client = createClient(); client.on('error', (err) => console.error('Redis Client Error', err)); @@ -12,35 +13,49 @@ const connectRedis = async () => { }; export default async function handler(req, res) { + await connectRedis(); + try { - await connectRedis(); - // await client.del('preferredBrand'); - - let cachedData; - if (req.method === 'GET') { - cachedData = await client.get('preferredBrand'); - - if (!cachedData) { - const items = await odooApi( - 'GET', - '/api/v1/manufacture?level=prioritas' - ); - await client.set( - 'preferredBrand', - JSON.stringify(items), - 'EX', - 259200 // Expiry 3 hari - ); - cachedData = await client.get('preferredBrand'); + let params = '*:*'; + let sort = + 'sort=if(exists(sequence_i),0,1) asc,sequence_i asc, if(exists(image_s),0,1) asc '; + let rows = 20; + + if (req.query.params) { + rows = 20; + switch (req.query.params) { + case 'level_s': + params = 'level_s:prioritas'; + break; + case 'search': + params = `name_s:"${req.query.q.toLowerCase()}"`; + sort = ''; + rows = 1; + break; + default: + params = `name_s:${req.query.params}`.toLowerCase(); } - const data = cachedData ? JSON.parse(cachedData) : null; - res.status(200).json({ data }); - } else { - res.setHeader('Allow', ['GET']); - res.status(405).end(`Method ${req.method} Not Allowed`); } + if (req.query.rows) rows = req.query.rows; + + const url = `${SOLR_HOST}/solr/brands/select?q=${params}&q.op=OR&indent=true&rows=${rows}&${sort}`; + const brands = await axios(url); + const dataBrands = responseMap(brands.data.response.docs); + + res.status(200).json(dataBrands); } catch (error) { - console.error('Error interacting with Redis:', error); - res.status(500).json({ error: 'Error interacting with Redis' }); + console.error('Error fetching data from Solr:', error); + res.status(500).json({ error: 'Internal Server Error' }); } } + +const responseMap = (brands) => { + return brands.map((brand) => { + return { + id: brand.id, + name: brand.display_name_s, + logo: brand.image_s || '', + sequence: brand.sequence_i || '', + }; + }); +}; -- cgit v1.2.3 From d47c3be772016dfcc2906ff024d53e9a2b27876e Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Sat, 16 Nov 2024 11:07:24 +0700 Subject: add tag google --- src/pages/_document.jsx | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/pages/_document.jsx b/src/pages/_document.jsx index 6af6294f..4b67c3f9 100644 --- a/src/pages/_document.jsx +++ b/src/pages/_document.jsx @@ -115,6 +115,19 @@ export default function MyDocument() { }} /> +