diff options
| author | it-fixcomart <it@fixcomart.co.id> | 2024-09-24 16:12:50 +0700 |
|---|---|---|
| committer | it-fixcomart <it@fixcomart.co.id> | 2024-09-24 16:12:50 +0700 |
| commit | 36601aba6017aeef16f89351eb487238402ab52e (patch) | |
| tree | 45bc3c78d5f36299922e6c03050e061b86a0d1e7 | |
| parent | 1e834e45f9a11911036496151b71f99de480862e (diff) | |
<iman> update Perapihan Tag
21 files changed, 1043 insertions, 735 deletions
@@ -0,0 +1,45 @@ +SELF_HOST=http://localhost:2100 +# SELF_HOST=http://192.168.23.178:2100 +# ODOO_API_HOST=http://192.168.23.191:8069 +# ODOO_API_HOST=http://192.168.23.251:8069 +# ODOO_API_HOST=http://192.168.23.73:8069 +# ODOO_API_HOST=https://erp.indoteknik.com +ODOO_API_HOST=http://localhost:8069 +SOLR_HOST=http://34.101.189.218:8983 +# SOLR_HOST=http://192.168.23.5:8983 +# SOLR_HOST=http://localhost:8983 + +RAJA_ONGKIR_HOST='https://pro.rajaongkir.com' +RAJA_ONGKIR_KEY='7ac9883688da043b50cc32f0e3070bb6' + +MAIL_PORT=465 +MAIL_HOST=smtp.gmail.com +MAIL_USER=sales@indoteknik.com +MAIL_PASS=#&Win*%a3C%^FF + +MIDTRANS_HOST=https://api.sandbox.midtrans.com +MIDTRANS_ENV=development +MIDTRANS_SERVER_KEY=SB-Mid-server-n-V7d2c2ZB0SVED29OyCWVYp +MIDTRANS_CLIENT_KEY=SB-Mid-client-IsVHHv5mzPqNV7bs + +RECAPTCHA_GOOGLE=6LdajH0lAAAAAI6kCVHjR9rHuD22Pf-X7XakSQ_P + +NEXTAUTH_URL=http://localhost:2100 + +PPN=1.11 + +# GOOGLE_CLIENT_ID="396438712998-308hckshgeekr34phr5jskj1f6qltfvk.apps.googleusercontent.com" +# GOOGLE_CLIENT_SECRET=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 +GOOGLE_CLIENT_ID="140590317805-5k5v3pjb7gcv4rcefgt8udgmn3hllv0q.apps.googleusercontent.com" +GOOGLE_CLIENT_SECRET=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 + +JWT_SECRET=NTNv7j0TuYARvmNMmWXo6fKvM4o6nvaUi9ryX38ZHL1bkrnD1ObOQ8JAUmHCBq7Iy7otZcyAagBLHVKvvYaIpmMuxmARQ97jUVG16Jkpkp1wXOPsrF9zwew6TpczyHkHgX5EuLg2MeBuiTqJACs1J0apruOOJCggOtkjB4c + +NEXT_PUBLIC_SELF_HOST=$SELF_HOST +NEXT_PUBLIC_ODOO_API_HOST=$ODOO_API_HOST +NEXT_PUBLIC_MIDTRANS_CLIENT_KEY=$MIDTRANS_CLIENT_KEY +NEXT_PUBLIC_RECAPTCHA_GOOGLE=$RECAPTCHA_GOOGLE +NEXT_PUBLIC_RAJA_ONGKIR_HOST=$RAJA_ONGKIR_HOST +NEXT_PUBLIC_RAJA_ONGKIR_KEY=$RAJA_ONGKIR_KEY +NEXT_PUBLIC_ODOO_HOST=$ODOO_API_HOST +NEXT_PUBLIC_PPN=$PPN
\ No newline at end of file diff --git a/src-migrate/modules/page-content/index.tsx b/src-migrate/modules/page-content/index.tsx index 547b1957..af597641 100644 --- a/src-migrate/modules/page-content/index.tsx +++ b/src-migrate/modules/page-content/index.tsx @@ -1,44 +1,45 @@ -import { useMemo } from "react" -import { useQuery } from "react-query" -import { PageContentProps } from "~/types/pageContent" -import { getPageContent } from "~/services/pageContent" +import { useMemo } from 'react'; +import { useQuery } from 'react-query'; +import { PageContentProps } from '~/types/pageContent'; +import { getPageContent } from '~/services/pageContent'; type Props = { - path: string -} + path: string; +}; const PageContent = ({ path }: Props) => { - const { data, isLoading } = useQuery<PageContentProps>(`page-content:${path}`, async () => await getPageContent({ path })) + const { data, isLoading } = useQuery<PageContentProps>( + `page-content:${path}`, + async () => await getPageContent({ path }) + ); const parsedContent = useMemo<string>(() => { - if (!data) return '' + if (!data) return ''; return data.content.replaceAll( 'src="/web/image', `src="${process.env.NEXT_PUBLIC_ODOO_API_HOST}/web/image` - ) - }, [data]) + ); + }, [data]); + console.log('parsedContent', parsedContent); + if (isLoading) return <PageContentSkeleton />; - if (isLoading) return <PageContentSkeleton /> - - return ( - <div dangerouslySetInnerHTML={{ __html: parsedContent || '' }}></div> - ) -} + return <div dangerouslySetInnerHTML={{ __html: parsedContent || '' }}></div>; +}; const PageContentSkeleton = () => ( - <div className="animate-pulse grid gap-y-4"> - <div className="w-full h-10 bg-gray-300 rounded" /> - <div className="h-2" /> - <div className="w-full h-4 bg-gray-300 rounded" /> - <div className="w-full h-4 bg-gray-300 rounded" /> - <div className="w-full h-4 bg-gray-300 rounded" /> - <div className="w-8/12 h-4 bg-gray-300 rounded" /> - <div className="h-2" /> - <div className="w-full h-4 bg-gray-300 rounded" /> - <div className="w-full h-4 bg-gray-300 rounded" /> - <div className="w-full h-4 bg-gray-300 rounded" /> - <div className="w-1/2 h-4 bg-gray-300 rounded" /> + <div className='animate-pulse grid gap-y-4'> + <div className='w-full h-10 bg-gray-300 rounded' /> + <div className='h-2' /> + <div className='w-full h-4 bg-gray-300 rounded' /> + <div className='w-full h-4 bg-gray-300 rounded' /> + <div className='w-full h-4 bg-gray-300 rounded' /> + <div className='w-8/12 h-4 bg-gray-300 rounded' /> + <div className='h-2' /> + <div className='w-full h-4 bg-gray-300 rounded' /> + <div className='w-full h-4 bg-gray-300 rounded' /> + <div className='w-full h-4 bg-gray-300 rounded' /> + <div className='w-1/2 h-4 bg-gray-300 rounded' /> </div> -) +); -export default PageContent
\ No newline at end of file +export default PageContent; diff --git a/src-migrate/modules/product-promo/components/Item.tsx b/src-migrate/modules/product-promo/components/Item.tsx index b396160f..4b345654 100644 --- a/src-migrate/modules/product-promo/components/Item.tsx +++ b/src-migrate/modules/product-promo/components/Item.tsx @@ -1,34 +1,35 @@ -import style from '../styles/item.module.css' +import style from '../styles/item.module.css'; -import { Tooltip } from '@chakra-ui/react' +import { Tooltip } from '@chakra-ui/react'; -import Image from '~/components/ui/image' -import { IProductVariantPromo } from '~/types/promotion' +import Image from '~/components/ui/image'; +import { IProductVariantPromo } from '~/types/promotion'; type Props = { - variant: IProductVariantPromo, - isFree?: boolean -} + variant: IProductVariantPromo; + isFree?: boolean; +}; -const ProductPromoItem = ({ - variant, - isFree = false -}: Props) => { +const ProductPromoItem = ({ variant, isFree = false }: Props) => { return ( <div className={style.item}> <div className={style.image}> - <Image src={variant.image || '/images/noimage.jpeg'} alt={variant.display_name} width={120} height={120} quality={100} /> + <Image + src={variant.image || '/images/noimage.jpeg'} + alt={variant.display_name} + width={120} + height={120} + quality={85} + /> <div className={style.quantity}> {variant.qty} pcs {isFree ? '(free)' : ''} </div> </div> <Tooltip label={variant.display_name} placement='top' fontSize='sm'> - <div className={style.name}> - {variant.name} - </div> + <div className={style.name}>{variant.name}</div> </Tooltip> </div> - ) -} + ); +}; -export default ProductPromoItem
\ No newline at end of file +export default ProductPromoItem; diff --git a/src-migrate/modules/promo/components/Hero.tsx b/src-migrate/modules/promo/components/Hero.tsx index 97cbe0b7..7d0aad11 100644 --- a/src-migrate/modules/promo/components/Hero.tsx +++ b/src-migrate/modules/promo/components/Hero.tsx @@ -3,34 +3,34 @@ import 'swiper/css'; import Image from 'next/image'; import { useEffect, useMemo } from 'react'; import { useQuery } from 'react-query'; -import { Swiper, SwiperProps, SwiperSlide } from 'swiper/react'; +import { Swiper, SwiperProps, SwiperSlide } from 'swiper/react'; import style from '../styles/hero.module.css'; import 'swiper/css/navigation'; import 'swiper/css/pagination'; -import { Navigation, Pagination, Autoplay } from 'swiper'; +import { Navigation, Pagination, Autoplay } from 'swiper'; import MobileView from '../../../../src/core/components/views/MobileView'; import DesktopView from '@/core/components/views/DesktopView'; -import {bannerApi} from '../../../../src/api/bannerApi' +import { bannerApi } from '../../../../src/api/bannerApi'; interface IPromotionProgram { headlineBanner: string; descriptionBanner: string; - image: string ; + image: string; name: string; } const swiperBanner: SwiperProps = { - modules:[Navigation, Pagination, Autoplay], + modules: [Navigation, Pagination, Autoplay], autoplay: { delay: 6000, - disableOnInteraction: false + disableOnInteraction: false, }, loop: true, className: 'h-[400px] w-full', slidesPerView: 1, spaceBetween: 10, - pagination:true, -} + pagination: true, +}; const swiperBannerMob = { autoplay: { delay: 6000, @@ -43,7 +43,10 @@ const swiperBannerMob = { }; const Hero = () => { - const heroBanner = useQuery('allPromo', bannerApi({ type: 'banner-semua-promo' })); + const heroBanner = useQuery( + 'allPromo', + bannerApi({ type: 'banner-semua-promo' }) + ); const banners: IPromotionProgram[] = useMemo( () => heroBanner?.data || [], @@ -54,52 +57,60 @@ const Hero = () => { ...swiperBannerMob, pagination: { dynamicBullets: false, clickable: true }, }; - + return ( <> <DesktopView> <div className={style['wrapper']}> - <Swiper {...swiperBanner}> - {banners?.map((banner, index) => ( - <SwiperSlide key={index} className='flex flex-row'> - <div className={style['desc-section']}> - <div className={style['title']}>{banner?.headlineBanner? banner?.headlineBanner : "Pasti Hemat & Untung Selama Belanja di Indoteknik.com!"}</div> - <div className='h-4' /> - <div className={style['subtitle']}>{banner?.descriptionBanner? banner?.descriptionBanner : "Cari paket yang kami sediakan dengan penawaran harga & Nikmati kemudahan dalam setiap transaksi dengan fitur lengkap Pembayaran hingga barang sampai!"}</div> + <Swiper {...swiperBanner}> + {banners?.map((banner, index) => ( + <SwiperSlide key={index} className='flex flex-row'> + <div className={style['desc-section']}> + <div className={style['title']}> + {banner?.headlineBanner + ? banner?.headlineBanner + : 'Pasti Hemat & Untung Selama Belanja di Indoteknik.com!'} </div> - <div className={style['banner-section']}> - <Image - src={banner.image} - alt={banner.name} - width={666} - height={450} - quality={90} - className='w-full h-full object-fit object-center rounded-2xl' /> + <div className='h-4' /> + <div className={style['subtitle']}> + {banner?.descriptionBanner + ? banner?.descriptionBanner + : 'Cari paket yang kami sediakan dengan penawaran harga & Nikmati kemudahan dalam setiap transaksi dengan fitur lengkap Pembayaran hingga barang sampai!'} </div> - </SwiperSlide> - ))} - </Swiper> - </div> - </DesktopView> - <MobileView> - <Swiper {...swiperBannerMobile}> - {banners?.map((banner, index) => ( - <SwiperSlide key={index}> - <Image - width={439} - height={150} - quality={100} - src={banner?.image} - alt={banner?.name} - className='w-full h-full object-cover object-center rounded-2xl' - /> + </div> + <div className={style['banner-section']}> + <Image + src={banner.image} + alt={banner.name} + width={666} + height={450} + quality={85} + className='w-full h-full object-fit object-center rounded-2xl' + /> + </div> </SwiperSlide> ))} </Swiper> - + </div> + </DesktopView> + <MobileView> + <Swiper {...swiperBannerMobile}> + {banners?.map((banner, index) => ( + <SwiperSlide key={index}> + <Image + width={439} + height={150} + quality={85} + src={banner?.image} + alt={banner?.name} + className='w-full h-full object-cover object-center rounded-2xl' + /> + </SwiperSlide> + ))} + </Swiper> </MobileView> </> - ) -} + ); +}; -export default Hero
\ No newline at end of file +export default Hero; diff --git a/src-migrate/modules/promo/components/PromotinProgram.jsx b/src-migrate/modules/promo/components/PromotinProgram.jsx index 33839944..43e4eedf 100644 --- a/src-migrate/modules/promo/components/PromotinProgram.jsx +++ b/src-migrate/modules/promo/components/PromotinProgram.jsx @@ -1,9 +1,7 @@ import React from 'react'; import Image from 'next/image'; -import { InfoIcon } from "lucide-react"; -import MobileView from '../../../../src/core/components/views/MobileView'; -import DesktopView from '@/core/components/views/DesktopView'; -import { Swiper, SwiperProps, SwiperSlide } from 'swiper/react'; +import { InfoIcon } from 'lucide-react'; +import { Swiper, SwiperProps, SwiperSlide } from 'swiper/react'; import 'swiper/css'; import useDevice from '@/core/hooks/useDevice'; @@ -11,9 +9,12 @@ const PromotionProgram = ({ selectedPromo, onSelectPromo }) => { const { isMobile } = useDevice(); return ( <> - <div className="text-h-sm md:text-h-lg font-semibold py-4">Serba Serbi Promo</div> + <h1 className='text-h-sm md:text-h-lg font-semibold py-4'> + {' '} + Serba Serbi Promo + </h1> <div className='px-4 sm:px-0'> - {/* <div className='w-full h-full '> + {/* <div className='w-full h-full '> <div onClick={() => onSelectPromo('Diskon')} className={`border p-2 flex items-center gap-x-2 rounded-lg cursor-pointer ${selectedPromo === 'Diskon' ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-200 text-gray-900'}`} @@ -39,93 +40,147 @@ const PromotionProgram = ({ selectedPromo, onSelectPromo }) => { </div> </div> </div> */} - - <Swiper slidesPerView={isMobile ? 1.3 : 3} spaceBetween={10}> - <SwiperSlide> - <div className='w-full h-full '> - <div - onClick={() => onSelectPromo('Bundling')} - className={`border h-full p-1 flex items-center gap-x-2 rounded-lg cursor-pointer ${selectedPromo === 'Bundling' ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-200 text-gray-900'}`} - > - <div> - <Image - width={24} - height={24} - quality={100} - src='/images/icon_promo/silat.svg' - alt='' - className='h-12 w-12 rounded' - /> - </div> - <div > - <div className='flex w-full flex-row items-center justify-start'> - <h1 className={`mr-1 font-semibold text-base ${selectedPromo === 'Bundling' ? 'text-red-500' : 'text-gray-900'}`}>Paket Silat</h1> - <InfoIcon className='mt-[1px] text-red-500' size={14} /> - </div> - <p className={`text-xs md:text-sm ${selectedPromo === 'Bundling' ? 'text-red-500' : 'text-gray-500'}`}> - Pilihan bundling barang kombinasi Silat. - </p> + + <Swiper slidesPerView={isMobile ? 1.3 : 3} spaceBetween={10}> + <SwiperSlide> + <div className='w-full h-full '> + <div + onClick={() => onSelectPromo('Bundling')} + className={`border h-full p-1 flex items-center gap-x-2 rounded-lg cursor-pointer ${ + selectedPromo === 'Bundling' + ? 'bg-red-50 border-red-500 text-red-500' + : 'border-gray-200 text-gray-900' + }`} + > + <div> + <Image + width={24} + height={24} + quality={85} + src='/images/icon_promo/silat.svg' + alt='' + className='h-12 w-12 rounded' + /> + </div> + <div> + <div className='flex w-full flex-row items-center justify-start'> + <h1 + className={`mr-1 font-semibold text-base ${ + selectedPromo === 'Bundling' + ? 'text-red-500' + : 'text-gray-900' + }`} + > + Paket Silat + </h1> + <InfoIcon className='mt-[1px] text-red-500' size={14} /> </div> + <p + className={`text-xs md:text-sm ${ + selectedPromo === 'Bundling' + ? 'text-red-500' + : 'text-gray-500' + }`} + > + Pilihan bundling barang kombinasi Silat. + </p> </div> </div> - </SwiperSlide> - <SwiperSlide> - <div className='w-full h-full '> - <div - onClick={() => onSelectPromo('Loading')} - className={`border p-2 h-full flex items-center gap-x-2 rounded-lg cursor-pointer ${selectedPromo === 'Loading' ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-200 text-gray-900'}`} - > - <div> - <Image - width={24} - height={24} - quality={100} - src='/images/icon_promo/barong.svg' - alt='' - className='h-12 w-12 rounded' - /> - </div> - <div> - <div className='flex w-full flex-row items-center justify-start'> - <h1 className={`mr-1 font-semibold text-base ${selectedPromo === 'Loading' ? 'text-red-500' : 'text-gray-900'}`}>Paket Barong</h1> - <InfoIcon className='mt-[1px] text-red-500' size={14} /> - </div> - <p className={`text-xs md:text-sm ${selectedPromo === 'Loading' ? 'text-red-500' : 'text-gray-500'}`}> - Beli banyak barang/partai barang borong. - </p> + </div> + </SwiperSlide> + <SwiperSlide> + <div className='w-full h-full '> + <div + onClick={() => onSelectPromo('Loading')} + className={`border p-2 h-full flex items-center gap-x-2 rounded-lg cursor-pointer ${ + selectedPromo === 'Loading' + ? 'bg-red-50 border-red-500 text-red-500' + : 'border-gray-200 text-gray-900' + }`} + > + <div> + <Image + width={24} + height={24} + quality={85} + src='/images/icon_promo/barong.svg' + alt='' + className='h-12 w-12 rounded' + /> + </div> + <div> + <div className='flex w-full flex-row items-center justify-start'> + <h1 + className={`mr-1 font-semibold text-base ${ + selectedPromo === 'Loading' + ? 'text-red-500' + : 'text-gray-900' + }`} + > + Paket Barong + </h1> + <InfoIcon className='mt-[1px] text-red-500' size={14} /> </div> + <p + className={`text-xs md:text-sm ${ + selectedPromo === 'Loading' + ? 'text-red-500' + : 'text-gray-500' + }`} + > + Beli banyak barang/partai barang borong. + </p> </div> </div> - </SwiperSlide> - <SwiperSlide> - <div className='w-full h-full '> - <div - onClick={() => onSelectPromo('Merchandise')} - className={`border p-2 h-full flex items-center gap-x-2 rounded-lg cursor-pointer ${selectedPromo === 'Merchandise' ? 'bg-red-50 border-red-500 text-red-500' : 'border-gray-200 text-gray-900'}`} - > - <div> - <Image - width={24} - height={24} - quality={100} - src='/images/icon_promo/angklung.svg' - alt='' - className='h-12 w-12 rounded' - /> - </div> - <div > - <div className='flex w-full flex-row items-center justify-start '> - <h1 className={`mr-1 font-semibold text-base ${selectedPromo === 'Merchandise' ? 'text-red-500' : 'text-gray-900'}`}>Paket Angklung</h1> - <InfoIcon className='mt-[1px] text-red-500' size={14} /> - </div> - <p className={` m1 text-xs md:text-sm ${selectedPromo === 'Merchandise' ? 'text-red-500' : 'text-gray-500'}`}> - Gratis barang promosi/merchandise menang langsung. - </p> + </div> + </SwiperSlide> + <SwiperSlide> + <div className='w-full h-full '> + <div + onClick={() => onSelectPromo('Merchandise')} + className={`border p-2 h-full flex items-center gap-x-2 rounded-lg cursor-pointer ${ + selectedPromo === 'Merchandise' + ? 'bg-red-50 border-red-500 text-red-500' + : 'border-gray-200 text-gray-900' + }`} + > + <div> + <Image + width={24} + height={24} + quality={85} + src='/images/icon_promo/angklung.svg' + alt='' + className='h-12 w-12 rounded' + /> + </div> + <div> + <div className='flex w-full flex-row items-center justify-start '> + <h1 + className={`mr-1 font-semibold text-base ${ + selectedPromo === 'Merchandise' + ? 'text-red-500' + : 'text-gray-900' + }`} + > + Paket Angklung + </h1> + <InfoIcon className='mt-[1px] text-red-500' size={14} /> </div> + <p + className={` m1 text-xs md:text-sm ${ + selectedPromo === 'Merchandise' + ? 'text-red-500' + : 'text-gray-500' + }`} + > + Gratis barang promosi/merchandise menang langsung. + </p> </div> </div> - </SwiperSlide> - </Swiper> + </div> + </SwiperSlide> + </Swiper> </div> </> ); diff --git a/src-migrate/modules/promo/components/Voucher.tsx b/src-migrate/modules/promo/components/Voucher.tsx index 510fe746..034d13e9 100644 --- a/src-migrate/modules/promo/components/Voucher.tsx +++ b/src-migrate/modules/promo/components/Voucher.tsx @@ -55,15 +55,18 @@ const VoucherComponent = () => { slidesPerView: isMobile ? 1.2 : 3.2, spaceBetween: 2, }; - - const dataVouchers = useMemo(() => voucherQuery?.data || [], [voucherQuery?.data]); - const vouchers = auth?.id? listVouchers : dataVouchers; + const dataVouchers = useMemo( + () => voucherQuery?.data || [], + [voucherQuery?.data] + ); + const vouchers = auth?.id ? listVouchers : dataVouchers; const copyText = (text: string) => { if (navigator.clipboard && navigator.clipboard.writeText) { - navigator.clipboard.writeText(text) + navigator.clipboard + .writeText(text) .then(() => { toast({ title: 'Salin ke papan klip', @@ -72,7 +75,7 @@ const VoucherComponent = () => { duration: 3000, isClosable: true, position: 'top', - }) + }); }) .catch(() => { fallbackCopyTextToClipboard(text); @@ -80,10 +83,10 @@ const VoucherComponent = () => { } else { fallbackCopyTextToClipboard(text); } - } + }; const fallbackCopyTextToClipboard = (text: string) => { - const textArea = document.createElement("textarea"); + const textArea = document.createElement('textarea'); textArea.value = text; // Tambahkan style untuk menyembunyikan textArea secara visual textArea.style.position = 'fixed'; @@ -108,23 +111,26 @@ const VoucherComponent = () => { duration: 3000, isClosable: true, position: 'top', - }) + }); } catch (err) { console.error('Fallback: Oops, unable to copy', err); } document.body.removeChild(textArea); - } + }; return ( <> - <div className={style['title']}>Pakai Voucher Belanja</div> + <h1 className={style['title']}>Pakai Voucher Belanja</h1> <div className='h-6' /> {voucherQuery?.isLoading && ( <div className='grid grid-cols-3 gap-x-4 animate-pulse'> {Array.from({ length: 3 }).map((_, index) => ( - <div key={index} className='w-full h-[160px] bg-gray-200 rounded-xl' /> + <div + key={index} + className='w-full h-[160px] bg-gray-200 rounded-xl' + /> ))} </div> )} @@ -134,17 +140,36 @@ const VoucherComponent = () => { {vouchers?.map((voucher) => ( <SwiperSlide key={voucher.id} className='pb-2'> <div className={style['voucher-card']}> - <Image src={voucher?.image} alt={voucher?.name} width={128} height={128} className={style['voucher-image']} /> + <Image + src={voucher?.image} + alt={voucher?.name} + width={128} + height={128} + className={style['voucher-image']} + /> <div className={style['voucher-content']}> - <div className={style['voucher-title']}>{voucher?.name}</div> - <div className={style['voucher-desc']}>{voucher?.description}</div> + <div className={style['voucher-title']}> + {voucher?.name} + </div> + <div className={style['voucher-desc']}> + {voucher?.description} + </div> <div className={style['voucher-bottom']}> <div> - <div className={style['voucher-code-desc']}>Kode Promo</div> - <div className={style['voucher-code']}>{voucher?.code}</div> + <div className={style['voucher-code-desc']}> + Kode Promo + </div> + <div className={style['voucher-code']}> + {voucher?.code} + </div> </div> - <button className={style['voucher-copy']} onClick={() => copyText(voucher?.code)}>Salin</button> + <button + className={style['voucher-copy']} + onClick={() => copyText(voucher?.code)} + > + Salin + </button> </div> </div> </div> @@ -154,7 +179,7 @@ const VoucherComponent = () => { </div> )} </> - ) -} + ); +}; -export default VoucherComponent +export default VoucherComponent; diff --git a/src-migrate/modules/register/components/FormBisnis.tsx b/src-migrate/modules/register/components/FormBisnis.tsx index 5ee19e58..e4cf3442 100644 --- a/src-migrate/modules/register/components/FormBisnis.tsx +++ b/src-migrate/modules/register/components/FormBisnis.tsx @@ -325,7 +325,7 @@ const form: React.FC<FormProps> = ({ className='w-full h-full ' width={800} height={800} - quality={100} + quality={85} /> </div> </BottomPopup> diff --git a/src-migrate/pages/shop/cart/index.tsx b/src-migrate/pages/shop/cart/index.tsx index 4768f62d..c5386c91 100644 --- a/src-migrate/pages/shop/cart/index.tsx +++ b/src-migrate/pages/shop/cart/index.tsx @@ -14,10 +14,10 @@ import clsxm from '~/libs/clsxm'; import useDevice from '@/core/hooks/useDevice'; import CartSummaryMobile from '~/modules/cart/components/CartSummaryMobile'; import Image from '~/components/ui/image'; -import { CartItem } from '~/types/cart' -import { deleteUserCart ,upsertUserCart } from '~/services/cart' +import { CartItem } from '~/types/cart'; +import { deleteUserCart, upsertUserCart } from '~/services/cart'; import { Trash2Icon } from 'lucide-react'; -import { useProductCartContext } from '@/contexts/ProductCartContext' +import { useProductCartContext } from '@/contexts/ProductCartContext'; const CartPage = () => { const router = useRouter(); @@ -26,11 +26,11 @@ const CartPage = () => { const [isSelectedAll, setIsSelectedAll] = useState(false); const [isButtonChek, setIsButtonChek] = useState(false); const [buttonSelectNow, setButtonSelectNow] = useState(true); - const [isLoad, setIsLoad] = useState<boolean>(false) - const [isLoadDelete, setIsLoadDelete] = useState<boolean>(false) + const [isLoad, setIsLoad] = useState<boolean>(false); + const [isLoadDelete, setIsLoadDelete] = useState<boolean>(false); const { loadCart, cart, summary, updateCartItem } = useCartStore(); const useDivvice = useDevice(); - const { setRefreshCart } = useProductCartContext() + const { setRefreshCart } = useProductCartContext(); const [isTop, setIsTop] = useState(true); const [hasChanged, setHasChanged] = useState(false); const prevCartRef = useRef<CartItem[] | null>(null); @@ -64,18 +64,19 @@ const CartPage = () => { const hasSelectedChanged = () => { if (prevCartRef.current && cart) { const prevCart = prevCartRef.current; - return cart.products.some((item, index) => - prevCart[index] && prevCart[index].selected !== item.selected + return cart.products.some( + (item, index) => + prevCart[index] && prevCart[index].selected !== item.selected ); } return false; }; if (hasSelectedChanged()) { - setHasChanged(true) + setHasChanged(true); // Perform necessary actions here if selection has changed - }else{ - setHasChanged(false) + } else { + setHasChanged(false); } prevCartRef.current = cart ? [...cart.products] : null; @@ -83,35 +84,38 @@ const CartPage = () => { const hasSelectedPromo = useMemo(() => { if (!cart) return false; - return cart.products.some(item => item.cart_type === 'promotion' && item.selected); + 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(item => item.selected && item.price.price_discount === 0); + return cart.products.some( + (item) => item.selected && item.price.price_discount === 0 + ); }, [cart]); const hasSelectedAll = useMemo(() => { if (!cart || !Array.isArray(cart.products)) return false; - return cart.products.every(item => item.selected); + return cart.products.every((item) => item.selected); }, [cart]); - useEffect(() => { const updateCartItems = async () => { if (typeof auth === 'object' && cart) { - const upsertPromises = cart.products.map(item => + const upsertPromises = cart.products.map((item) => upsertUserCart({ userId: auth.id, type: item.cart_type, id: item.id, qty: item.quantity, - selected: item.selected + selected: item.selected, }) ); try { @@ -128,7 +132,7 @@ const CartPage = () => { const handleCheckout = () => { router.push('/shop/checkout'); - } + }; const handleQuotation = () => { if (hasSelectedPromo || !hasSelected) { @@ -136,54 +140,53 @@ const CartPage = () => { } else { router.push('/shop/quotation'); } - } + }; const handleChange = async (e: React.ChangeEvent<HTMLInputElement>) => { - - if (cart) { const updatedCart = { ...cart, - products: cart.products.map(item => ({ + products: cart.products.map((item) => ({ ...item, - selected: !hasSelectedAll - })) + selected: !hasSelectedAll, + })), }; - - updateCartItem(updatedCart); - if(hasSelectedAll){ + + updateCartItem(updatedCart); + if (hasSelectedAll) { setIsSelectedAll(false); - }else{ + } else { setIsSelectedAll(true); } } }; - const handleDelete = async () => { if (typeof auth !== 'object' || !cart) return; - setIsLoadDelete(true) + setIsLoadDelete(true); for (const item of cart.products) { - if(item.selected === true){ - await deleteUserCart(auth.id, [item.cart_id]) - await loadCart(auth.id) + if (item.selected === true) { + await deleteUserCart(auth.id, [item.cart_id]); + await loadCart(auth.id); } } - setIsLoadDelete(false) - setRefreshCart(true) - } + setIsLoadDelete(false); + setRefreshCart(true); + }; return ( <> - <div className={`${isTop ? 'border-b-[0px]' : 'border-b-[1px]'} sticky md:top-[157px] flex-col bg-white py-4 border-gray-300 z-50 sm:w-full md:w-3/4`}> - <div className={`${style['title']}`}>Keranjang Belanja</div> + <div + className={`${ + isTop ? 'border-b-[0px]' : 'border-b-[1px]' + } sticky md:top-[157px] flex-col bg-white py-4 border-gray-300 z-50 sm:w-full md:w-3/4`} + > + <h1 className={`${style['title']}`}>Keranjang Belanja</h1> <div className='h-2' /> <div className={`flex items-center object-center justify-between `}> <div className='flex items-center object-center'> - {isLoad && ( - <Spinner className='my-auto' size='sm' /> - )} + {isLoad && <Spinner className='my-auto' size='sm' />} {!isLoad && ( <Checkbox borderColor='gray.600' @@ -193,34 +196,31 @@ const CartPage = () => { onChange={handleChange} /> )} - <p className='p-2 text-caption-2'> - {hasSelectedAll ? "Uncheck all" : "Select all"} - </p> + <p className='p-2 text-caption-2'> + {hasSelectedAll ? 'Uncheck all' : 'Select all'} + </p> + </div> + <div className='delate all flex items-center object-center'> + <Tooltip + label={clsxm({ + 'Tidak ada item yang dipilih': !hasSelected, + })} + > + <Button + bg='#fadede' + variant='outline' + colorScheme='red' + w='full' + isDisabled={!hasSelected} + onClick={handleDelete} + > + {isLoadDelete && <Spinner size='xs' />} + {!isLoadDelete && <Trash2Icon size={16} />} + <p className='text-sm ml-2'>Hapus Barang</p> + </Button> + </Tooltip> </div> - <div className='delate all flex items-center object-center'> - <Tooltip - label={clsxm({ - 'Tidak ada item yang dipilih': !hasSelected, - })} - > - <Button - bg='#fadede' - variant='outline' - colorScheme='red' - w='full' - isDisabled={!hasSelected} - onClick={handleDelete} - > - {isLoadDelete && <Spinner size='xs' />} - {!isLoadDelete && <Trash2Icon size={16} />} - <p className='text-sm ml-2'> - Hapus Barang - </p> - </Button> - </Tooltip> - </div> </div> - </div> <div className={style['content']}> @@ -274,7 +274,13 @@ const CartPage = () => { <CartSummary {...summary} isLoaded={!!cart} /> )} - <div className={isStepApproval ? style['summary-buttons-step-approval'] : style['summary-buttons']}> + <div + className={ + isStepApproval + ? style['summary-buttons-step-approval'] + : style['summary-buttons'] + } + > <Tooltip label={ hasSelectedPromo && @@ -315,4 +321,4 @@ const CartPage = () => { ); }; -export default CartPage;
\ No newline at end of file +export default CartPage; diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx index 8f024d86..4688b15b 100644 --- a/src/core/components/elements/Footer/BasicFooter.jsx +++ b/src/core/components/elements/Footer/BasicFooter.jsx @@ -175,7 +175,7 @@ const CustomerGuide = () => ( <div> <div className={headerClassName}>Bantuan & Panduan</div> <ul className='flex flex-col gap-y-3'> - <li > + <li> <InternalItemLink href='/metode-pembayaran'> Metode Pembayaran </InternalItemLink> @@ -395,7 +395,7 @@ const Payments = () => ( alt='Metode Pembayaran - Indoteknik' width={512} height={512} - quality={100} + quality={85} className='w-full' /> </div> @@ -409,7 +409,7 @@ const Shippings = () => ( alt='Jasa Pengiriman - Indoteknik' width={512} height={512} - quality={100} + quality={85} className='w-full' /> </div> @@ -423,7 +423,7 @@ const Secures = () => ( alt='Keamanan Belanja - Indoteknik' width={512} height={512} - quality={100} + quality={85} className='w-full' /> </div> diff --git a/src/core/components/elements/Navbar/NavbarDesktop.jsx b/src/core/components/elements/Navbar/NavbarDesktop.jsx index 2a51c41f..d480aa8d 100644 --- a/src/core/components/elements/Navbar/NavbarDesktop.jsx +++ b/src/core/components/elements/Navbar/NavbarDesktop.jsx @@ -5,7 +5,7 @@ import { createSlug } from '@/core/utils/slug'; import whatsappUrl from '@/core/utils/whatsappUrl'; import IndoteknikLogo from '@/images/logo.png'; import Cardheader from '@/lib/cart/components/Cartheader'; -import Quotationheader from "../../../../../src/lib/quotation/components/Quotationheader.jsx" +import Quotationheader from '../../../../../src/lib/quotation/components/Quotationheader.jsx'; import Category from '@/lib/category/components/Category'; import { useProductCartContext } from '@/contexts/ProductCartContext'; import { @@ -30,7 +30,7 @@ import { MenuList, useDisclosure, } from '@chakra-ui/react'; -import style from "./style/NavbarDesktop.module.css"; +import style from './style/NavbarDesktop.module.css'; import useTransactions from '@/lib/transaction/hooks/useTransactions'; import { useCartStore } from '~/modules/cart/stores/useCartStore'; @@ -43,7 +43,7 @@ const NavbarDesktop = () => { const [cartCount, setCartCount] = useState(0); const [quotationCount, setQuotationCount] = useState(0); - const [pendingTransactions, setPendingTransactions] = useState([]) + const [pendingTransactions, setPendingTransactions] = useState([]); const [templateWA, setTemplateWA] = useState(null); const [payloadWA, setPayloadWa] = useState(null); const [urlPath, setUrlPath] = useState(null); @@ -52,14 +52,17 @@ const NavbarDesktop = () => { const { product } = useProductContext(); const { isOpen, onOpen, onClose } = useDisclosure(); - + const query = { context: 'quotation', - site: - (auth?.webRole === null && auth?.site ? auth.site : null), + site: auth?.webRole === null && auth?.site ? auth.site : null, }; - - const { transactions } = useTransactions({ query }); + let transactions = null; + + if (auth) { + transactions = useTransactions({ query }); + } + const data = transactions?.data?.saleOrders.filter( (transaction) => transaction.status === 'draft' ); @@ -106,9 +109,8 @@ const NavbarDesktop = () => { useEffect(() => { setPendingTransactions(data); - }, [transactions.data]); - - + }, [transactions?.data]); + useEffect(() => { if (router.pathname === '/shop/product/[slug]') { setPayloadWa({ @@ -117,11 +119,11 @@ const NavbarDesktop = () => { url: createSlug('/shop/product/', product?.name, product?.id, true), }); setTemplateWA('product'); - + setUrlPath(router.asPath); } }, [product, router]); - + useEffect(() => { const handleCartChange = () => { const cart = async () => { @@ -130,14 +132,14 @@ const NavbarDesktop = () => { }; cart(); }; - handleCartChange(); - + handleCartChange(); + window.addEventListener('localStorageChange', handleCartChange); - + return () => { window.removeEventListener('localStorageChange', handleCartChange); }; - }, [transactions.data, cart]); + }, [transactions?.data, cart]); useEffect(() => { const handleQuotationChange = () => { @@ -154,7 +156,7 @@ const NavbarDesktop = () => { window.removeEventListener('localStorageChange', handleQuotationChange); }; }, [pendingTransactions]); - + return ( <DesktopView> <TopBanner onLoad={handleTopBannerLoad} /> @@ -174,7 +176,7 @@ const NavbarDesktop = () => { > <div className='flex gap-x-1'> <div>Fitur Layanan </div> - <ChevronDownIcon className='w-5'/> + <ChevronDownIcon className='w-5' /> </div> </MenuButton> <MenuList @@ -217,7 +219,10 @@ const NavbarDesktop = () => { <Search /> </div> <div className='flex gap-x-4 items-center'> - <Quotationheader quotationCount={quotationCount} data={pendingTransactions} /> + <Quotationheader + quotationCount={quotationCount} + data={pendingTransactions} + /> <Cardheader cartCount={cartCount} /> @@ -271,29 +276,30 @@ const NavbarDesktop = () => { </div> </div> <div className='w-6/12 flex px-1 divide-x divide-gray_r-6'> - - <Link - href="/shop/promo" + <Link + href='/shop/promo' className={`${ router.asPath === '/shop/promo' && 'bg-gray_r-3' } flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition group relative`} // Added relative position - target="_blank" - rel="noreferrer" + target='_blank' + rel='noreferrer' > - {showPopup && ( - <div className='w-full h-full relative justify-end items-start'> + {showPopup && ( + <div className='w-full h-full relative justify-end items-start'> <Image - src='/images/penawaran-terbatas.jpg' - alt='penawaran terbatas' - width={1440} - height={160} - quality={100} - // className={`fixed ${isTop ? 'md:top-[145px] lg:top-[160px] ' : 'lg:top-[85px] top-[80px]'} rounded-3xl md:left-1/4 lg:left-1/4 xl:left-1/4 left-2/3 w-40 h-12 p-2 z-50 transition-all duration-300 animate-pulse`} - className={`inline-block relative -top-8 transition-all duration-300 z-20 animate-pulse`} - /> + src='/images/penawaran-terbatas.jpg' + alt='penawaran terbatas' + width={1440} + height={160} + quality={85} + // className={`fixed ${isTop ? 'md:top-[145px] lg:top-[160px] ' : 'lg:top-[85px] top-[80px]'} rounded-3xl md:left-1/4 lg:left-1/4 xl:left-1/4 left-2/3 w-40 h-12 p-2 z-50 transition-all duration-300 animate-pulse`} + className={`inline-block relative -top-8 transition-all duration-300 z-20 animate-pulse`} + /> </div> - )} - <span className="absolute inset-0 flex justify-center items-center group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200 z-10">Semua Promo</span> + )} + <span className='absolute inset-0 flex justify-center items-center group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200 z-10'> + Semua Promo + </span> </Link> {/* {showPopup && router.pathname === '/' && ( <div className={`fixed ${isTop ? 'top-[170px]' : 'top-[90px]'} rounded-3xl left-[700px] w-fit object-center bg-green-50 text-xs font-medium text-green-700 ring-1 ring-inset ring-green-600/20 text-center p-2 z-50 transition-all duration-300`}> @@ -302,17 +308,18 @@ const NavbarDesktop = () => { </p> </div> )} */} - <Link href='/shop/brands' - className={`${ + className={`${ router.asPath === '/shop/brands' && 'bg-gray_r-3' } p-4 flex-1 flex justify-center items-center !text-gray_r-12/80 hover:bg-gray_r-3 idt-transition group`} target='_blank' rel='noreferrer' > - <span className="group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200">Semua Brand</span> + <span className='group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200'> + Semua Brand + </span> </Link> <Link href='/shop/search?orderBy=stock' @@ -323,7 +330,9 @@ const NavbarDesktop = () => { target='_blank' rel='noreferrer' > - <span className="group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200">Ready Stock</span> + <span className='group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200'> + Ready Stock + </span> </Link> <Link href='https://blog.indoteknik.com/' @@ -331,7 +340,9 @@ const NavbarDesktop = () => { target='_blank' rel='noreferrer noopener' > - <span className="group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200">Blog Indoteknik</span> + <span className='group-hover:scale-105 group-hover:text-red-500 transition-transform duration-200'> + Blog Indoteknik + </span> </Link> {/* <Link href='/video' diff --git a/src/lib/home/components/BannerSection.jsx b/src/lib/home/components/BannerSection.jsx index 2010503d..f83c36fc 100644 --- a/src/lib/home/components/BannerSection.jsx +++ b/src/lib/home/components/BannerSection.jsx @@ -1,12 +1,12 @@ -import Link from '@/core/components/elements/Link/Link' -import Image from 'next/image' +import Link from '@/core/components/elements/Link/Link'; +import Image from 'next/image'; -const { useQuery } = require('react-query') -const { default: bannerSectionApi } = require('../api/bannerSectionApi') +const { useQuery } = require('react-query'); +const { default: bannerSectionApi } = require('../api/bannerSectionApi'); const BannerSection = () => { - const fetchBannerSection = async () => await bannerSectionApi() - const bannerSection = useQuery('bannerSection', fetchBannerSection) + const fetchBannerSection = async () => await bannerSectionApi(); + const bannerSection = useQuery('bannerSection', fetchBannerSection); return ( bannerSection.data && @@ -17,7 +17,7 @@ const BannerSection = () => { <Image width={1024} height={512} - quality={100} + quality={85} src={banner.image} alt={banner.name} className='h-auto w-full rounded' @@ -26,7 +26,7 @@ const BannerSection = () => { ))} </div> ) - ) -} + ); +}; -export default BannerSection +export default BannerSection; diff --git a/src/lib/home/components/CategoryDynamic.jsx b/src/lib/home/components/CategoryDynamic.jsx index 79092c9d..49a9a93f 100644 --- a/src/lib/home/components/CategoryDynamic.jsx +++ b/src/lib/home/components/CategoryDynamic.jsx @@ -3,13 +3,12 @@ import { fetchCategoryManagementSolr } from '../api/categoryManagementApi'; import NextImage from 'next/image'; import Link from 'next/link'; import { createSlug } from '@/core/utils/slug'; -import odooApi from '@/core/api/odooApi'; import { Skeleton } from '@chakra-ui/react'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; import 'swiper/css/navigation'; import 'swiper/css/pagination'; -import { Navigation, Pagination, Autoplay } from 'swiper'; +import { Pagination } from 'swiper'; const CategoryDynamic = () => { const [categoryManagement, setCategoryManagement] = useState([]); @@ -73,9 +72,9 @@ const CategoryDynamic = () => { <Skeleton key={category.id} isLoaded={!isLoading}> <div key={category.id}> <div className='bagian-judul flex flex-row justify-start items-center gap-3 mb-4 mt-4'> - <div className='font-semibold sm:text-h-lg mr-2'> + <h1 className='font-semibold text-[14px] sm:text-h-lg mr-2'> {category.name} - </div> + </h1> {/* <Skeleton isLoaded={countLevel1 != 0}> <p className={`text-gray_r-10 text-sm`}>{countLevel1} Produk tersedia</p> </Skeleton> */} @@ -113,9 +112,9 @@ const CategoryDynamic = () => { className='object-fit p-4' /> <div className='bagian-judul flex flex-col justify-center items-start gap-2 ml-2'> - <div className='font-semibold text-lg mr-2'> + <h2 className='font-semibold text-lg mr-2'> {subCategory?.name} - </div> + </h2> {/* <Skeleton isLoaded={countLevel2 != 0}> <p className={`text-gray_r-10 text-sm`}> {countLevel2} Produk tersedia @@ -157,9 +156,9 @@ const CategoryDynamic = () => { height={40} /> <div className='bagian-judul flex flex-col justify-center items-center gap-2 break-words line-clamp-2 group-hover:text-red-500'> - <div className='font-semibold line-clamp-2 group-hover:text-red-500 text-sm mr-2'> + <h3 className='font-semibold line-clamp-2 group-hover:text-red-500 text-sm mr-2'> {childCategory.name} - </div> + </h3> </div> </Link> </div> diff --git a/src/lib/home/components/CategoryDynamicMobile.jsx b/src/lib/home/components/CategoryDynamicMobile.jsx index af151df2..4a8f13cf 100644 --- a/src/lib/home/components/CategoryDynamicMobile.jsx +++ b/src/lib/home/components/CategoryDynamicMobile.jsx @@ -59,9 +59,9 @@ const CategoryDynamicMobile = () => { categoryManagement?.map((category) => ( <div key={category.id}> <div className='bagian-judul flex flex-row justify-between items-center gap-3 mb-4 mt-4'> - <div className='font-semibold sm:text-h-sm mr-2'> - {category?.name} - </div> + <h1 className='font-semibold text-[14px] sm:text-h-lg mr-2'> + {category.name} + </h1> <Link href={createSlug( '/shop/category/', @@ -98,9 +98,9 @@ const CategoryDynamicMobile = () => { className='' /> <div className='bagian-judul flex flex-col justify-center items-start gap-1 ml-2'> - <div className='font-semibold text-[10px] line-clamp-1'> + <h2 className='font-semibold text-[10px] line-clamp-1'> {index?.name} - </div> + </h2> </div> </div> </div> @@ -131,9 +131,9 @@ const CategoryDynamicMobile = () => { className='p-2' /> <div className='bagian-judul flex flex-col justify-center items-start gap-1 break-words line-clamp-2 group-hover:text-red-500'> - <div className='font-semibold line-clamp-2 group-hover:text-red-500 text-[10px]'> + <h3 className='font-semibold line-clamp-2 group-hover:text-red-500 text-[10px]'> {x?.name} - </div> + </h3> </div> </Link> </div> diff --git a/src/lib/home/components/CategoryHomeId.jsx b/src/lib/home/components/CategoryHomeId.jsx index 71428e27..9f436dac 100644 --- a/src/lib/home/components/CategoryHomeId.jsx +++ b/src/lib/home/components/CategoryHomeId.jsx @@ -1,13 +1,15 @@ -import { LazyLoadComponent } from 'react-lazy-load-image-component' -import useCategoryHomeId from '../hooks/useCategoryHomeId' -import CategoryHome from './CategoryHome' +import { LazyLoadComponent } from 'react-lazy-load-image-component'; +import useCategoryHomeId from '../hooks/useCategoryHomeId'; +import CategoryHome from './CategoryHome'; const CategoryHomeId = () => { - const { categoryHomeIds } = useCategoryHomeId() + const { categoryHomeIds } = useCategoryHomeId(); return ( <div> - <div className='font-semibold sm:text-h-lg mb-6 px-4 sm:px-0'>Kategori Pilihan</div> + <h1 className='font-semibold text-[14px] sm:text-h-lg mb-6 px-4 sm:px-0'> + Kategori Pilihan + </h1> <div className='flex flex-col gap-y-10'> {categoryHomeIds.data?.map((id) => ( <LazyLoadComponent key={id}> @@ -16,7 +18,7 @@ const CategoryHomeId = () => { ))} </div> </div> - ) -} + ); +}; -export default CategoryHomeId +export default CategoryHomeId; diff --git a/src/lib/home/components/CategoryPilihan.jsx b/src/lib/home/components/CategoryPilihan.jsx index 6dbf771e..2e5ca721 100644 --- a/src/lib/home/components/CategoryPilihan.jsx +++ b/src/lib/home/components/CategoryPilihan.jsx @@ -1,123 +1,168 @@ -import Image from 'next/image' -import useCategoryHome from '../hooks/useCategoryHome' -import Link from '@/core/components/elements/Link/Link' -import { createSlug } from '@/core/utils/slug' +import Image from 'next/image'; +import useCategoryHome from '../hooks/useCategoryHome'; +import Link from '@/core/components/elements/Link/Link'; +import { createSlug } from '@/core/utils/slug'; import { useEffect, useState } from 'react'; import { bannerApi } from '../../../api/bannerApi'; -const { useQuery } = require('react-query') +const { useQuery } = require('react-query'); import { HeroBannerSkeleton } from '../../../components/skeleton/BannerSkeleton'; import useCategoryPilihan from '../hooks/useCategoryPilihan'; -import useDevice from '@/core/hooks/useDevice' +import useDevice from '@/core/hooks/useDevice'; import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/css'; const CategoryPilihan = ({ id, categories }) => { - const { isDesktop, isMobile } = useDevice() - const { categoryPilihan } = useCategoryPilihan(); - const heroBanner = useQuery('categoryPilihan', bannerApi({ type: 'banner-category-list' })); - return ( - categoryPilihan.length > 0 && ( - <section> - {isDesktop && ( - <div> - <div className='flex flex-row items-center mb-4'> - <div className='font-semibold sm:text-h-lg mr-2'>LOB Kategori Pilihan</div> - <p className='text-gray_r-10 text-sm'>200 Rb+ Produk Unggulan & 800+ Brand Rekomendasi tersedia!</p> - </div> - {heroBanner.data && - heroBanner.data?.length > 0 && ( - <div className='flex w-full h-full justify-center mb-4 bg-cover bg-center'> - <Link key={heroBanner.data[0].id} href={heroBanner.data[0].url}> - <Image - width={1260} - height={170} - quality={100} - src={heroBanner.data[0].image} - alt={heroBanner.data[0].name} - className='h-full object-cover w-full' - /> - </Link> - </div> - )} - <div className="group/item grid grid-cols-6 gap-y-2 w-full h-full col-span-2 "> - {categoryPilihan?.data?.map((category) => ( - <div key={category.id} className="KartuInti h-48 w-60 max-w-sm lg:max-w-full flex flex-col border-[1px] border-gray-200 relative group"> - <div className='KartuB absolute h-48 w-60 inset-0 flex items-center justify-center '> - <div className="group/edit flex items-center justify-end h-48 w-60 flex-col group-hover/item:visible"> - <div className=' h-36 flex justify-end items-end'> - <Image className='group-hover:scale-105 transition-transform duration-300 ' src={category?.image? category?.image : '/images/noimage.jpeg'} width={120} height={120} alt={category?.name} /> - </div> - <h2 className="text-gray-700 content-center h-12 border-t-[1px] px-1 w-60 border-gray-200 font-normal text-sm text-center">{category?.industries}</h2> - </div> - </div> - <div className='KartuA relative inset-0 flex h-36 w-60 items-center justify-center opacity-0 group-hover:opacity-75 group-hover:bg-[#E20613] transition-opacity '> - <Link - href={createSlug('/shop/lob/', category?.industries, category?.id)} - className='category-mega-box__parent text-white rounded-lg' - > - Lihat semua - </Link> - </div> - </div> - ))} + const { isDesktop, isMobile } = useDevice(); + const { categoryPilihan } = useCategoryPilihan(); + const heroBanner = useQuery( + 'categoryPilihan', + bannerApi({ type: 'banner-category-list' }) + ); + return ( + categoryPilihan.length > 0 && ( + <section> + {isDesktop && ( + <div> + <div className='flex flex-row items-center mb-4'> + <div className='font-semibold sm:text-h-lg mr-2'> + LOB Kategori Pilihan + </div> + <p className='text-gray_r-10 text-sm'> + 200 Rb+ Produk Unggulan & 800+ Brand Rekomendasi tersedia! + </p> + </div> + {heroBanner.data && heroBanner.data?.length > 0 && ( + <div className='flex w-full h-full justify-center mb-4 bg-cover bg-center'> + <Link key={heroBanner.data[0].id} href={heroBanner.data[0].url}> + <Image + width={1260} + height={170} + quality={85} + src={heroBanner.data[0].image} + alt={heroBanner.data[0].name} + className='h-full object-cover w-full' + /> + </Link> + </div> + )} + <div className='group/item grid grid-cols-6 gap-y-2 w-full h-full col-span-2 '> + {categoryPilihan?.data?.map((category) => ( + <div + key={category.id} + className='KartuInti h-48 w-60 max-w-sm lg:max-w-full flex flex-col border-[1px] border-gray-200 relative group' + > + <div className='KartuB absolute h-48 w-60 inset-0 flex items-center justify-center '> + <div className='group/edit flex items-center justify-end h-48 w-60 flex-col group-hover/item:visible'> + <div className=' h-36 flex justify-end items-end'> + <Image + className='group-hover:scale-105 transition-transform duration-300 ' + src={ + category?.image + ? category?.image + : '/images/noimage.jpeg' + } + width={120} + height={120} + alt={category?.name} + /> + </div> + <h2 className='text-gray-700 content-center h-12 border-t-[1px] px-1 w-60 border-gray-200 font-normal text-sm text-center'> + {category?.industries} + </h2> </div> + </div> + <div className='KartuA relative inset-0 flex h-36 w-60 items-center justify-center opacity-0 group-hover:opacity-75 group-hover:bg-[#E20613] transition-opacity '> + <Link + href={createSlug( + '/shop/lob/', + category?.industries, + category?.id + )} + className='category-mega-box__parent text-white rounded-lg' + > + Lihat semua + </Link> + </div> </div> - )} - {isMobile && ( - <div className='p-4'> - <div className='flex flex-row items-center mb-4'> - <div className='font-semibold sm:text-h-md mr-2'>LOB Kategori Pilihan</div> - {/* <p className='text-gray_r-10 text-sm'>200 Rb+ Produk Unggulan & 800+ Brand Rekomendasi tersedia!</p> */} + ))} + </div> + </div> + )} + {isMobile && ( + <div className='p-4'> + <div className='flex flex-row items-center mb-4'> + <div className='font-semibold sm:text-h-md mr-2'> + LOB Kategori Pilihan + </div> + {/* <p className='text-gray_r-10 text-sm'>200 Rb+ Produk Unggulan & 800+ Brand Rekomendasi tersedia!</p> */} + </div> + <div className='flex'> + {heroBanner.data && heroBanner.data?.length > 0 && ( + <div className=' object-fill '> + <Link + key={heroBanner.data[0].id} + href={heroBanner.data[0].url} + > + <Image + width={439} + height={150} + quality={85} + src={heroBanner.data[0].image} + alt={heroBanner.data[0].name} + className='object-cover' + /> + </Link> + </div> + )} + </div> + <Swiper slidesPerView={2.1} spaceBetween={10}> + {categoryPilihan?.data?.map((category) => ( + <SwiperSlide key={category.id}> + <div + key={category.id} + className='KartuInti mt-2 h-48 w-48 max-w-sm lg:max-w-full flex flex-col border-[1px] border-gray-200 relative group' + > + <div className='KartuB absolute h-48 w-48 inset-0 flex items-center justify-center '> + <div className='group/edit flex items-center justify-end h-48 w-48 flex-col group-hover/item:visible'> + <div className=' h-36 flex justify-end items-end'> + <Image + className='group-hover:scale-105 transition-transform duration-300 ' + src={ + category?.image + ? category?.image + : '/images/noimage.jpeg' + } + width={120} + height={120} + alt={category?.name} + /> + </div> + <h2 className='text-gray-700 content-center h-12 border-t-[1px] px-1 w-48 border-gray-200 font-normal text-sm text-center'> + {category?.industries} + </h2> + </div> </div> - <div className='flex'> - {heroBanner.data && - heroBanner.data?.length > 0 && ( - <div className=' object-fill '> - <Link key={heroBanner.data[0].id} href={heroBanner.data[0].url}> - <Image - width={439} - height={150} - quality={100} - src={heroBanner.data[0].image} - alt={heroBanner.data[0].name} - className='object-cover' - /> - </Link> - </div> - )} + <div className='KartuA relative inset-0 flex h-36 w-48 items-center justify-center opacity-0 group-hover:opacity-75 group-hover:bg-[#E20613] transition-opacity '> + <Link + href={createSlug( + '/shop/lob/', + category?.industries, + category?.id + )} + className='category-mega-box__parent text-white rounded-lg' + > + Lihat semua + </Link> </div> - <Swiper slidesPerView={2.1} spaceBetween={10}> - {categoryPilihan?.data?.map((category) => ( - <SwiperSlide key={category.id}> - <div key={category.id} className="KartuInti mt-2 h-48 w-48 max-w-sm lg:max-w-full flex flex-col border-[1px] border-gray-200 relative group"> - <div className='KartuB absolute h-48 w-48 inset-0 flex items-center justify-center '> - <div className="group/edit flex items-center justify-end h-48 w-48 flex-col group-hover/item:visible"> - <div className=' h-36 flex justify-end items-end'> - <Image className='group-hover:scale-105 transition-transform duration-300 ' src={category?.image? category?.image : '/images/noimage.jpeg'} width={120} height={120} alt={category?.name} /> - </div> - <h2 className="text-gray-700 content-center h-12 border-t-[1px] px-1 w-48 border-gray-200 font-normal text-sm text-center">{category?.industries}</h2> - </div> - </div> - <div className='KartuA relative inset-0 flex h-36 w-48 items-center justify-center opacity-0 group-hover:opacity-75 group-hover:bg-[#E20613] transition-opacity '> - <Link - href={createSlug('/shop/lob/', category?.industries, category?.id)} - className='category-mega-box__parent text-white rounded-lg' - > - Lihat semua - </Link> - </div> - </div> - </SwiperSlide> - ))} - - </Swiper> - - </div> - )} - </section> - - ) - ) -} + </div> + </SwiperSlide> + ))} + </Swiper> + </div> + )} + </section> + ) + ); +}; -export default CategoryPilihan +export default CategoryPilihan; diff --git a/src/lib/home/components/PromotionProgram.jsx b/src/lib/home/components/PromotionProgram.jsx index ae8d5d6f..ae06bd4d 100644 --- a/src/lib/home/components/PromotionProgram.jsx +++ b/src/lib/home/components/PromotionProgram.jsx @@ -1,13 +1,16 @@ -import Link from '@/core/components/elements/Link/Link' -import Image from 'next/image' +import Link from '@/core/components/elements/Link/Link'; +import Image from 'next/image'; import { bannerApi } from '@/api/bannerApi'; -import useDevice from '@/core/hooks/useDevice' +import useDevice from '@/core/hooks/useDevice'; import { Swiper, SwiperSlide } from 'swiper/react'; -import BannerPromoSkeleton from '../components/Skeleton/BannerPromoSkeleton'; -const { useQuery } = require('react-query') +import BannerPromoSkeleton from '../components/Skeleton/BannerPromoSkeleton'; +const { useQuery } = require('react-query'); const BannerSection = () => { - const promotionProgram = useQuery('promotionProgram', bannerApi({ type: 'banner-promotion' })); - const { isMobile, isDesktop } = useDevice() + const promotionProgram = useQuery( + 'promotionProgram', + bannerApi({ type: 'banner-promotion' }) + ); + const { isMobile, isDesktop } = useDevice(); if (promotionProgram.isLoading) { return <BannerPromoSkeleton />; @@ -16,60 +19,65 @@ const BannerSection = () => { return ( <div className='px-4 sm:px-0'> <div className='flex justify-between items-center mb-4 '> - <h1 className='font-semibold text-[14px] sm:text-h-lg'> <Link href='/shop/promo' className='!text-black font-semibold' >Promo Tersedia</Link></h1> + <h1 className='font-semibold text-[14px] sm:text-h-lg'> + {' '} + <Link href='/shop/promo' className='!text-black font-semibold'> + Promo Tersedia + </Link> + </h1> {isDesktop && ( <Link href='/shop/promo' className='!text-red-500 font-semibold'> - Lihat Semua - </Link> + Lihat Semua + </Link> )} {isMobile && ( - <Link href='/shop/promo' className='!text-red-500 font-semibold sm:text-h-sm'> - Lihat Semua - </Link> - )} - </div> - {isDesktop && (promotionProgram.data && - promotionProgram.data?.length > 0 && ( - <div className='grid grid-cols-3 sm:grid-cols-3 gap-4 rounded-md'> - {promotionProgram.data?.map((banner) => ( - <Link key={banner.id} href={banner.url}> - <Image - width={439} - height={150} - quality={100} - src={banner.image} - alt={banner.name} - className='h-auto w-full rounded hover:scale-105 transition duration-500 ease-in-out' - /> + <Link + href='/shop/promo' + className='!text-red-500 font-semibold sm:text-h-sm' + > + Lihat Semua </Link> - ))} + )} </div> - - ))} + {isDesktop && + promotionProgram.data && + promotionProgram.data?.length > 0 && ( + <div className='grid grid-cols-3 sm:grid-cols-3 gap-4 rounded-md'> + {promotionProgram.data?.map((banner) => ( + <Link key={banner.id} href={banner.url}> + <Image + width={439} + height={150} + quality={85} + src={banner.image} + alt={banner.name} + className='h-auto w-full rounded hover:scale-105 transition duration-500 ease-in-out' + /> + </Link> + ))} + </div> + )} -{isMobile && ( - - <Swiper slidesPerView={1.1} spaceBetween={8} freeMode> - {promotionProgram.data?.map((banner) => ( - <SwiperSlide key={banner.id}> - <Link key={banner.id} href={banner.url}> - <Image - width={439} - height={150} - quality={100} - src={banner.image} - alt={banner.name} - className='h-auto w-full rounded ' - /> - </Link> - </SwiperSlide> - ))} - </Swiper> - - )} + {isMobile && ( + <Swiper slidesPerView={1.1} spaceBetween={8} freeMode> + {promotionProgram.data?.map((banner) => ( + <SwiperSlide key={banner.id}> + <Link key={banner.id} href={banner.url}> + <Image + width={439} + height={150} + quality={85} + src={banner.image} + alt={banner.name} + className='h-auto w-full rounded ' + /> + </Link> + </SwiperSlide> + ))} + </Swiper> + )} </div> - - ) -} + ); +}; -export default BannerSection +export default BannerSection; diff --git a/src/lib/home/components/ServiceList.jsx b/src/lib/home/components/ServiceList.jsx index b8799d7d..5b16915d 100644 --- a/src/lib/home/components/ServiceList.jsx +++ b/src/lib/home/components/ServiceList.jsx @@ -1,5 +1,5 @@ -import Image from 'next/image' -import Link from '@/core/components/elements/Link/Link' +import Image from 'next/image'; +import Link from '@/core/components/elements/Link/Link'; const ServiceList = () => { return ( @@ -14,14 +14,16 @@ const ServiceList = () => { <Image width={24} height={24} - quality={100} + quality={85} src='/images/icon_service/ONE-STOP-SOLUTIONS.svg' alt='' className='h-20 w-20 rounded' /> </div> <div className=''> - <h1 className='text-gray-900 font-semibold text-base'>One Stop Solution</h1> + <h1 className='text-gray-900 font-semibold text-base'> + One Stop Solution + </h1> <p className='text-xs md:text-sm text-gray-500'> Temukan Solusi Lengkap Anda dalam Satu Tempat. </p> @@ -37,14 +39,16 @@ const ServiceList = () => { <Image width={24} height={24} - quality={100} + quality={85} src='/images/icon_service/WARRANTY.svg' alt='' className='h-20 w-20 rounded' /> </div> <div> - <h1 className='text-gray-900 font-semibold text-base'>Garansi Resmi</h1> + <h1 className='text-gray-900 font-semibold text-base'> + Garansi Resmi + </h1> <p className='text-xs md:text-sm text-gray-500'> Garansi Keaslian Barang dan Jaminan Purna Jual. </p> @@ -60,14 +64,16 @@ const ServiceList = () => { <Image width={24} height={24} - quality={100} + quality={85} src='/images/icon_service/DUE-PAYMENT.svg' alt='' className='h-20 w-20 rounded' /> </div> <div> - <h1 className='text-gray-900 font-semibold text-base'>Pembayaran Tempo</h1> + <h1 className='text-gray-900 font-semibold text-base'> + Pembayaran Tempo + </h1> <p className='text-xs md:text-sm text-gray-500'> Lebih mudah mengatur pembelian dengan pembayaran tempo. </p> @@ -83,14 +89,16 @@ const ServiceList = () => { <Image width={24} height={24} - quality={100} + quality={85} src='/images/icon_service/TAX.svg' alt='' className='h-20 w-20 rounded' /> </div> <div> - <h1 className='text-gray-900 font-semibold text-base'>Faktur Pajak</h1> + <h1 className='text-gray-900 font-semibold text-base'> + Faktur Pajak + </h1> <p className='text-xs md:text-sm text-gray-500'> Dapat Faktur pajak untuk setiap transaksi dengan indoteknik.com </p> @@ -99,7 +107,7 @@ const ServiceList = () => { </div> </div> </div> - ) -} + ); +}; -export default ServiceList +export default ServiceList; diff --git a/src/lib/quotation/components/Quotationheader.jsx b/src/lib/quotation/components/Quotationheader.jsx index 4529c977..cb7684ed 100644 --- a/src/lib/quotation/components/Quotationheader.jsx +++ b/src/lib/quotation/components/Quotationheader.jsx @@ -19,14 +19,22 @@ const Quotationheader = (quotationCount) => { context: 'quotation', site: auth?.webRole === null && auth?.site ? auth.site : null, }; - + const router = useRouter(); const [subTotal, setSubTotal] = useState(null); const [buttonLoading, SetButtonTerapkan] = useState(false); const itemLoading = [1, 2, 3]; const [countQuotation, setCountQuotation] = useState(null); - const { productCart, setProductCart, refreshCart, setRefreshCart, isLoading, setIsloading, productQuotation, setProductQuotation } = - useProductCartContext(); + const { + productCart, + setProductCart, + refreshCart, + setRefreshCart, + isLoading, + setIsloading, + productQuotation, + setProductQuotation, + } = useProductCartContext(); const [isHovered, setIsHovered] = useState(false); const [isTop, setIsTop] = useState(true); @@ -49,34 +57,44 @@ const Quotationheader = (quotationCount) => { refreshCartf(); } }; - let { transactions } = useTransactions({ query }); + let transactions = null; + + if (auth) { + transactions = useTransactions({ query }); + } const refreshCartf = useCallback(async () => { setIsloading(true); - let pendingTransactions = transactions?.data?.saleOrders.filter(transaction => transaction.status === 'draft'); + let pendingTransactions = transactions?.data?.saleOrders.filter( + (transaction) => transaction.status === 'draft' + ); setProductQuotation(pendingTransactions); - setCountQuotation(pendingTransactions?.length ? pendingTransactions?.length : pendingTransactions?.length); + setCountQuotation( + pendingTransactions?.length + ? pendingTransactions?.length + : pendingTransactions?.length + ); setIsloading(false); }, [setProductQuotation, setIsloading]); useEffect(() => { - if (!qotation) return + if (!qotation) return; - let calculateTotalDiscountAmount = 0 + let calculateTotalDiscountAmount = 0; for (const product of qotation) { - // if (qotation.quantity == '') continue - calculateTotalDiscountAmount += product.amountUntaxed + // if (qotation.quantity == '') continue + calculateTotalDiscountAmount += product.amountUntaxed; } - let subTotal = calculateTotalDiscountAmount - setSubTotal(subTotal) - }, [qotation]) + let subTotal = calculateTotalDiscountAmount; + setSubTotal(subTotal); + }, [qotation]); useEffect(() => { if (refreshCart) { refreshCartf(); } setRefreshCart(false); - }, [ refreshCartf, setRefreshCart]); + }, [refreshCartf, setRefreshCart]); useEffect(() => { setCountQuotation(quotationCount.quotationCount); @@ -95,7 +113,10 @@ const Quotationheader = (quotationCount) => { const handleCheckout = async () => { SetButtonTerapkan(true); - let checkoutAll = await odooApi('POST', `/api/v1/user/${auth.id}/cart/select-all`); + let checkoutAll = await odooApi( + 'POST', + `/api/v1/user/${auth.id}/cart/select-all` + ); router.push('/my/quotations'); }; @@ -150,7 +171,9 @@ const Quotationheader = (quotationCount) => { className='w-full max-w-md p-2 bg-white border border-gray-200 rounded-lg shadow overflow-hidden' > <div className='p-2 flex justify-between items-center'> - <h5 className='text-base font-semibold leading-none'>Daftar Quotation</h5> + <h5 className='text-base font-semibold leading-none'> + Daftar Quotation + </h5> </div> <hr className='mt-3 mb-3 border border-gray-100' /> <div className='flow-root max-h-[250px] overflow-y-auto'> @@ -158,7 +181,10 @@ const Quotationheader = (quotationCount) => { <div className='justify-center p-4'> <p className='text-gray-500 text-center '> Silahkan{' '} - <Link href='/login' className='text-red-600 underline leading-6'> + <Link + href='/login' + className='text-red-600 underline leading-6' + > Login </Link>{' '} Untuk Melihat Daftar Quotation Anda @@ -167,7 +193,11 @@ const Quotationheader = (quotationCount) => { )} {isLoading && itemLoading.map((item) => ( - <div key={item} role='status' className='max-w-sm animate-pulse'> + <div + key={item} + role='status' + className='max-w-sm animate-pulse' + > <div className='flex items-center space-x-4 mb- 2'> <div className='flex-shrink-0'> <PhotoIcon className='h-16 w-16 text-gray-500' /> @@ -189,43 +219,70 @@ const Quotationheader = (quotationCount) => { )} {auth && qotation.length > 0 && !isLoading && ( <> - <ul role='list' className='divide-y divide-gray-200 dark:divide-gray-700'> + <ul + role='list' + className='divide-y divide-gray-200 dark:divide-gray-700' + > {qotation && qotation?.map((product, index) => ( <> <li className='py-1 sm:py-2'> <div className='flex justify-between border p-2 flex-col gap-y-2 hover:border-red-500'> - <Link + <Link href={`/my/quotations/${product?.id}`} - className='hover:border-red-500' - > + className='hover:border-red-500' + > <div className='flex justify-between mb-2'> <div className='flex flex-row items-center'> - <p className='tanggal text-xs opacity-80 mr-[2px]'>Sales : </p> - <p className='tanggal text-xs text-red-500 font-semibold'>{product.sales}</p> + <p className='tanggal text-xs opacity-80 mr-[2px]'> + Sales :{' '} + </p> + <p className='tanggal text-xs text-red-500 font-semibold'> + {product.sales} + </p> </div> <div className='flex flex-row items-center'> - <p className='text-xs opacity-80 mr-[2px]'>Status :</p> - <p className='badge-red h-fit text-xs whitespace-nowrap'>Pending Quotation</p> + <p className='text-xs opacity-80 mr-[2px]'> + Status : + </p> + <p className='badge-red h-fit text-xs whitespace-nowrap'> + Pending Quotation + </p> </div> </div> <div className='flex justify-between mb-2'> <div className='flex flex-col items-start'> - <p className=' text-xs opacity-80 mr-[2px]'>No. Transaksi</p> - <p className=' text-sm text-red-500 font-semibold'> {product.name}</p> + <p className=' text-xs opacity-80 mr-[2px]'> + No. Transaksi + </p> + <p className=' text-sm text-red-500 font-semibold'> + {' '} + {product.name} + </p> </div> <div className='flex flex-col items-end'> - <p className='text-xs opacity-80 mr-[2px]'>No. Purchase Order</p> - <p className='font-semibold text-sm text-red-500'> {product.purchaseOrderName ? product.purchaseOrderName : '-'}</p> + <p className='text-xs opacity-80 mr-[2px]'> + No. Purchase Order + </p> + <p className='font-semibold text-sm text-red-500'> + {' '} + {product.purchaseOrderName + ? product.purchaseOrderName + : '-'} + </p> </div> </div> {/* <div className='my-0.5 h-0.5 bg-gray-200'></div> */} <hr className='mt-3 mb-3 border border-gray-100' /> <div className='bagian bawah flex justify-between mt-2'> - <p className='font-semibold text-sm'>Total</p> - <p className='font-semibold text-sm'>{currencyFormat(product.amountUntaxed)}</p> + <p className='font-semibold text-sm'> + Total + </p> + <p className='font-semibold text-sm'> + {currencyFormat(product.amountUntaxed)} + </p> </div> - </Link> + </Link> </div> </li> </> @@ -238,8 +295,12 @@ const Quotationheader = (quotationCount) => { {auth && qotation.length > 0 && !isLoading && ( <> <div className='mt-3 ml-1'> - <span className='text-gray-400 text-caption-2'>Subtotal Sebelum PPN : </span> - <span className='font-semibold text-red-600'>{currencyFormat(subTotal)}</span> + <span className='text-gray-400 text-caption-2'> + Subtotal Sebelum PPN :{' '} + </span> + <span className='font-semibold text-red-600'> + {currencyFormat(subTotal)} + </span> </div> <div className='mt-5 mb-2'> <button diff --git a/src/lib/review/components/CustomerReviews.jsx b/src/lib/review/components/CustomerReviews.jsx index 7cad52fb..a6e697f0 100644 --- a/src/lib/review/components/CustomerReviews.jsx +++ b/src/lib/review/components/CustomerReviews.jsx @@ -1,18 +1,23 @@ -import DesktopView from '@/core/components/views/DesktopView' -import MobileView from '@/core/components/views/MobileView' -import Image from 'next/image' -import { Swiper, SwiperSlide } from 'swiper/react' -import { Autoplay } from 'swiper' +import DesktopView from '@/core/components/views/DesktopView'; +import MobileView from '@/core/components/views/MobileView'; +import Image from 'next/image'; +import { Swiper, SwiperSlide } from 'swiper/react'; +import { Autoplay } from 'swiper'; -const { useQuery } = require('react-query') -const { getCustomerReviews } = require('../api/customerReviewsApi') +const { useQuery } = require('react-query'); +const { getCustomerReviews } = require('../api/customerReviewsApi'); const CustomerReviews = () => { - const { data: customerReviews } = useQuery('customerReviews', getCustomerReviews) + const { data: customerReviews } = useQuery( + 'customerReviews', + getCustomerReviews + ); return ( <div className='px-4 sm:px-0'> - <div className='font-semibold sm:text-h-lg mb-4'>Ulasan Konsumen Kami</div> + <h1 className='font-semibold text-[14px] sm:text-h-lg mb-4'> + Ulasan Konsumen Kami + </h1> <DesktopView> <Swiper slidesPerView={3.2} spaceBetween={16} {...swiperProps}> @@ -36,17 +41,17 @@ const CustomerReviews = () => { </Swiper> </MobileView> </div> - ) -} + ); +}; const swiperProps = { autoplay: { delay: 6000, - disableOnInteraction: false + disableOnInteraction: false, }, loop: true, - modules: [Autoplay] -} + modules: [Autoplay], +}; const Card = ({ customerReview }) => ( <div className='bg-gray-200 rounded-md px-5 py-6 shadow-md shadow-gray-500/20 h-full'> @@ -67,6 +72,6 @@ const Card = ({ customerReview }) => ( dangerouslySetInnerHTML={{ __html: customerReview.ulasan }} /> </div> -) +); -export default CustomerReviews +export default CustomerReviews; diff --git a/src/lib/tracking-order/component/TrackingOrder.jsx b/src/lib/tracking-order/component/TrackingOrder.jsx index 394979c1..8a7b2579 100644 --- a/src/lib/tracking-order/component/TrackingOrder.jsx +++ b/src/lib/tracking-order/component/TrackingOrder.jsx @@ -1,139 +1,161 @@ -import { yupResolver } from '@hookform/resolvers/yup' -import React, { useEffect, useState } from 'react' -import { useForm } from 'react-hook-form' -import * as Yup from 'yup' -import Manifest from '@/lib/treckingAwb/component/Manifest' -import { trackingOrder } from '../api/trackingOrder' -import { useQuery } from 'react-query' +import { yupResolver } from '@hookform/resolvers/yup'; +import React, { useEffect, useState } from 'react'; +import { useForm } from 'react-hook-form'; +import * as Yup from 'yup'; +import Manifest from '@/lib/treckingAwb/component/Manifest'; +import { trackingOrder } from '../api/trackingOrder'; +import { useQuery } from 'react-query'; import { Spinner } from '@chakra-ui/react'; import { Search } from 'lucide-react'; -import whatsappUrl from '@/core/utils/whatsappUrl'; -import Link from 'next/link' +import Link from 'next/link'; const TrackingOrder = () => { - const [idAWB, setIdAWB] = useState(null) - const [inputQuery, setInputQuery] = useState(null) - const [buttonClick, setButtonClick] = useState(false) - const [apiError, setApiError] = useState(null) // State to store API error message - - const closePopup = () => { - setIdAWB(null) - setButtonClick(false) - setInputQuery(null) - setApiError(null) // Reset error message on close - } - - const { - register, - handleSubmit, - formState: { errors }, - control, - reset - } = useForm({ - resolver: yupResolver(validationSchema), - defaultValues - }) - - const query = { - email: inputQuery?.email, - so: inputQuery?.id - } - - const { data: tracking, isLoading, isError, error } = useQuery( - ['tracking', query], - () => trackingOrder({ query: query }), - { - enabled: !!query.email && !!query.so, - onSuccess: (data) => { - if (buttonClick) { - if (data?.code === 403 || data?.code === 400 || data?.code === 404) { - setApiError(data?.description); - } else if (data?.pickings?.length > 0) { - setIdAWB(data.pickings[0]?.id); - } else { - setApiError('No pickings data available'); - } - setButtonClick(false); - setInputQuery(null); - } - }, + const [idAWB, setIdAWB] = useState(null); + const [inputQuery, setInputQuery] = useState(null); + const [buttonClick, setButtonClick] = useState(false); + const [apiError, setApiError] = useState(null); // State to store API error message + + const closePopup = () => { + setIdAWB(null); + setButtonClick(false); + setInputQuery(null); + setApiError(null); // Reset error message on close + }; + + const { + register, + handleSubmit, + formState: { errors }, + control, + reset, + } = useForm({ + resolver: yupResolver(validationSchema), + defaultValues, + }); + + const query = { + email: inputQuery?.email, + so: inputQuery?.id, + }; + + const { + data: tracking, + isLoading, + isError, + error, + } = useQuery(['tracking', query], () => trackingOrder({ query: query }), { + enabled: !!query.email && !!query.so, + onSuccess: (data) => { + if (buttonClick) { + if (data?.code === 403 || data?.code === 400 || data?.code === 404) { + setApiError(data?.description); + } else if (data?.pickings?.length > 0) { + setIdAWB(data.pickings[0]?.id); + } else { + setApiError('No pickings data available'); } - ); + setButtonClick(false); + setInputQuery(null); + } + }, + }); - const onSubmitHandler = async (values) => { - setInputQuery(values) - setButtonClick(true) - } + const onSubmitHandler = async (values) => { + setInputQuery(values); + setButtonClick(true); + }; - return ( - <div className='container mx-auto flex py-10 flex-col'> - <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Tracking Order</h1> - <div className='flex justify-start items-start'> - <span className='text-base w-full'> - {`Untuk melacak pesanan Anda, masukkan Nomor Transaksi di kotak bawah ini dan masukkan Email login anda lalu tekan tombol "Lacak". Nomor Transaksi ini dapat Anda lihat dalam menu `} - <Link href='/my/transactions' className='text-red-500'> - Daftar Transaksi - </Link> - {`. Jika mengalami kesulitan `} - <Link href='https://wa.me/6281717181922' target='_blank' rel='noreferrer' className='text-red-500'> - hubungi kami - </Link> - {`.`} - </span> + return ( + <div className='container mx-auto flex py-10 flex-col'> + <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'> + Tracking Order + </h1> + <div className='flex justify-start items-start'> + <p className='text-base w-full'> + {`Untuk melacak pesanan Anda, masukkan Nomor Transaksi di kotak bawah ini dan masukkan Email login anda lalu tekan tombol "Lacak". Nomor Transaksi ini dapat Anda lihat dalam menu `} + <Link href='/my/transactions' className='text-red-500'> + Daftar Transaksi + </Link> + {`. Jika mengalami kesulitan `} + <Link + href='https://wa.me/6281717181922' + target='_blank' + rel='noreferrer' + className='text-red-500' + > + hubungi kami + </Link> + {`.`} + </p> + </div> + <div> + <form + onSubmit={handleSubmit(onSubmitHandler)} + className='flex mt-4 flex-row w-full ' + > + <div className='w-[90%] grid grid-cols-2 gap-4'> + <div className='flex flex-col '> + <label className='form-label mb-2'>ID Pesanan*</label> + <input + {...register('id')} + placeholder='dapat dilihat pada email konfirmasi anda' + type='text' + className='form-input mb-2' + aria-invalid={errors.id?.message} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.id?.message} + </div> </div> - <div> - <form onSubmit={handleSubmit(onSubmitHandler)} className='flex mt-4 flex-row w-full '> - <div className='w-[90%] grid grid-cols-2 gap-4'> - <div className='flex flex-col '> - <label className='form-label mb-2'>ID Pesanan*</label> - <input - {...register('id')} - placeholder='dapat dilihat pada email konfirmasi anda' - type='text' - className='form-input mb-2' - aria-invalid={errors.id?.message} - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.id?.message}</div> - </div> - <div className='flex flex-col '> - <label className='form-label mb-2'>Email Penagihan*</label> - <input - {...register('email')} - placeholder='Email yang anda gunakan saat pembayaran' - type='text' - className='form-input' - aria-invalid={errors.email?.message} - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div> - </div> - </div> - <div className={` ${errors.id?.message ? 'mt-2' : 'mt-5'} flex items-center ml-4`}> - <button - type='submit' - className='bg-red-600 border border-red-600 rounded-md text-sm text-white w-24 h-11 mb-1 content-center flex flex-row justify-center items-center' - > - {isLoading && <Spinner size='xs' className='mr-2'/>} - {!isLoading && <Search size={16} strokeWidth={1} className='mr-2'/>} - <p>Lacak</p> - </button> - </div> - </form> - {/* Display the API error message */} - {apiError && <div className='text-danger-500 mt-4'>{apiError}</div>} - <Manifest idAWB={idAWB} closePopup={closePopup} /> + <div className='flex flex-col '> + <label className='form-label mb-2'>Email Penagihan*</label> + <input + {...register('email')} + placeholder='Email yang anda gunakan saat pembayaran' + type='text' + className='form-input' + aria-invalid={errors.email?.message} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.email?.message} + </div> </div> - </div> - ) -} + </div> + <div + className={` ${ + errors.id?.message ? 'mt-2' : 'mt-5' + } flex items-center ml-4`} + > + <button + type='submit' + className='bg-red-600 border border-red-600 rounded-md text-sm text-white w-24 h-11 mb-1 content-center flex flex-row justify-center items-center' + > + {isLoading && <Spinner size='xs' className='mr-2' />} + {!isLoading && ( + <Search size={16} strokeWidth={1} className='mr-2' /> + )} + <p>Lacak</p> + </button> + </div> + </form> + {/* Display the API error message */} + {apiError && <div className='text-danger-500 mt-4'>{apiError}</div>} + <Manifest idAWB={idAWB} closePopup={closePopup} /> + </div> + </div> + ); +}; const validationSchema = Yup.object().shape({ - email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'), - id: Yup.string().required('Harus di-isi'), -}) + email: Yup.string() + .email('Format harus seperti contoh@email.com') + .required('Harus di-isi'), + id: Yup.string().required('Harus di-isi'), +}); const defaultValues = { - email: '', - id: '' -} + email: '', + id: '', +}; -export default TrackingOrder +export default TrackingOrder; diff --git a/src/pages/my/profile.jsx b/src/pages/my/profile.jsx index 25c3a608..7cf1bcbb 100644 --- a/src/pages/my/profile.jsx +++ b/src/pages/my/profile.jsx @@ -1,41 +1,44 @@ -import Divider from '@/core/components/elements/Divider/Divider' -import AppLayout from '@/core/components/layouts/AppLayout' -import BasicLayout from '@/core/components/layouts/BasicLayout' -import DesktopView from '@/core/components/views/DesktopView' -import MobileView from '@/core/components/views/MobileView' -import useAuth from '@/core/hooks/useAuth' -import CompanyProfile from '@/lib/auth/components/CompanyProfile' -import IsAuth from '@/lib/auth/components/IsAuth' -import Menu from '@/lib/auth/components/Menu' -import PersonalProfile from '@/lib/auth/components/PersonalProfile' +import Divider from '@/core/components/elements/Divider/Divider'; +import AppLayout from '@/core/components/layouts/AppLayout'; +import BasicLayout from '@/core/components/layouts/BasicLayout'; +import DesktopView from '@/core/components/views/DesktopView'; +import MobileView from '@/core/components/views/MobileView'; +import useAuth from '@/core/hooks/useAuth'; +import CompanyProfile from '@/lib/auth/components/CompanyProfile'; +import IsAuth from '@/lib/auth/components/IsAuth'; +import Menu from '@/lib/auth/components/Menu'; +import PersonalProfile from '@/lib/auth/components/PersonalProfile'; +import Seo from '@/core/components/Seo'; export default function Profile() { - const auth = useAuth() + const auth = useAuth(); return ( - <IsAuth> - <MobileView> - <AppLayout title='Akun Saya'> - <PersonalProfile /> - <Divider /> - {auth?.parentId && <CompanyProfile />} - </AppLayout> - </MobileView> - - <DesktopView> - <BasicLayout> - <div className='container mx-auto flex py-10'> - <div className='w-3/12 pr-4'> - <Menu /> - </div> - <div className='w-9/12 bg-white border border-gray_r-6 rounded'> + <> + <Seo title='Profile - Indoteknik.com' /> + <IsAuth> + <MobileView> + <AppLayout title='Akun Saya'> <PersonalProfile /> <Divider /> {auth?.parentId && <CompanyProfile />} + </AppLayout> + </MobileView> - </div> - </div> - </BasicLayout> - </DesktopView> - </IsAuth> - ) + <DesktopView> + <BasicLayout> + <div className='container mx-auto flex py-10'> + <div className='w-3/12 pr-4'> + <Menu /> + </div> + <div className='w-9/12 bg-white border border-gray_r-6 rounded'> + <PersonalProfile /> + <Divider /> + {auth?.parentId && <CompanyProfile />} + </div> + </div> + </BasicLayout> + </DesktopView> + </IsAuth> + </> + ); } |
