diff options
Diffstat (limited to 'src')
31 files changed, 665 insertions, 378 deletions
diff --git a/src/api/promoApi.js b/src/api/promoApi.js index 3f85db8e..8adc4647 100644 --- a/src/api/promoApi.js +++ b/src/api/promoApi.js @@ -22,7 +22,9 @@ export const fetchPromoItemsSolr = async (type, start, rows) => { // let rows = 10 try { const queryParams = new URLSearchParams({ q: type }); - const response = await fetch(`/solr/promotion_program_lines/select?${queryParams.toString()}&rows=${rows}&start=${start}&${sort}`); + const response = await fetch( + `/solr/promotion_program_lines/select?${queryParams.toString()}&rows=${rows}&start=${start}&${sort}&fq=active_b%3Atrue` + ); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx index d804bd24..579846e0 100644 --- a/src/core/components/elements/Footer/BasicFooter.jsx +++ b/src/core/components/elements/Footer/BasicFooter.jsx @@ -229,32 +229,32 @@ const Form = () => ( <div className={headerClassName}>Formulir</div> <ul className='flex flex-col gap-y-3'> <li> - <InternalItemLink href='/my/request-for-quotation'> + <InternalItemLink href='/request-for-quotation'> Request for Quotation </InternalItemLink> </li> <li> - <InternalItemLink href='/my/kunjungan-sales'> + <InternalItemLink href='/kunjungan-sales'> Kunjungan Sales </InternalItemLink> </li> <li> - <InternalItemLink href='/my/kunjungan-service'> + <InternalItemLink href='/kunjungan-service'> Kunjungan Service </InternalItemLink> </li> <li> - <InternalItemLink href='/my/pembayaran-tempo'> + <InternalItemLink href='/pembayaran-tempo'> Pembayaran Tempo </InternalItemLink> </li> <li> - <InternalItemLink href='/my/surat-dukungan'> + <InternalItemLink href='/surat-dukungan'> Surat Dukungan </InternalItemLink> </li> <li> - <InternalItemLink href='/my/daftar-merchant'> + <InternalItemLink href='/daftar-merchant'> Daftar Merchant </InternalItemLink> </li> diff --git a/src/core/utils/googleTag.js b/src/core/utils/googleTag.js index 96a6bd2e..f1550071 100644 --- a/src/core/utils/googleTag.js +++ b/src/core/utils/googleTag.js @@ -1,3 +1,5 @@ +const PPN = process.env.NEXT_PUBLIC_PPN + const mapVariants = (variants) => { return variants.map((variant) => { const res = { @@ -22,7 +24,7 @@ const sumTotal = (variants) => { 0 ) let subtotal = totalPurchase - totalDiscount - let tax = Math.round(subtotal * 0.11) + let tax = Math.round(subtotal * (PPN - 1)) let grandTotal = subtotal + tax return { totalPurchase: totalPurchase, diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index c6aaa596..1e25d959 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -27,6 +27,7 @@ import CardProdcuctsList from '@/core/components/elements/Product/cartProductsLi // import cardProdcuctsList from '@/core/components/elements/Product/cartProductsList' const Cart = () => { + const PPN = process.env.NEXT_PUBLIC_PPN const router = useRouter() const [products, setProducts] = useState(null) const [isLoading, setIsLoading] = useState(true) @@ -97,7 +98,7 @@ const Cart = () => { if (product.canBuy == false) { toggleSelected(product.id) } - let priceBeforeTax = product.price.price / 1.11 + let priceBeforeTax = product.price.price / PPN calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity calculateTotalDiscountAmount += diff --git a/src/lib/cart/components/CartOld.jsx b/src/lib/cart/components/CartOld.jsx index 718541af..358efa49 100644 --- a/src/lib/cart/components/CartOld.jsx +++ b/src/lib/cart/components/CartOld.jsx @@ -20,6 +20,7 @@ import whatsappUrl from '@/core/utils/whatsappUrl' import useAuth from '@/core/hooks/useAuth' const Cart = () => { + const PPN = process.env.NEXT_PUBLIC_PPN const router = useRouter() const [products, setProducts] = useState(null) const auth = useAuth() @@ -67,7 +68,7 @@ const Cart = () => { }) if (!product.selected) continue - let priceBeforeTax = product.price.price / 1.11 + let priceBeforeTax = product.price.price / PPN calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity calculateTotalDiscountAmount += diff --git a/src/lib/cart/components/Cartheader.jsx b/src/lib/cart/components/Cartheader.jsx index 1c30bb13..6d4e2679 100644 --- a/src/lib/cart/components/Cartheader.jsx +++ b/src/lib/cart/components/Cartheader.jsx @@ -14,6 +14,7 @@ const { ShoppingCartIcon, PhotoIcon } = require('@heroicons/react/24/outline'); const { default: Link } = require('next/link'); const Cardheader = (cartCount) => { + const PPN = process.env.NEXT_PUBLIC_PPN const router = useRouter(); const [subTotal, setSubTotal] = useState(null); const [buttonLoading, SetButtonTerapkan] = useState(false); @@ -66,7 +67,7 @@ const Cardheader = (cartCount) => { for (const product of products) { if (product.quantity == '') continue; - let priceBeforeTax = product.price.price / 1.11; + let priceBeforeTax = product.price.price / PPN; calculateTotalPriceBeforeTax += priceBeforeTax * product.quantity; calculateTotalTaxAmount += (product.price.price - priceBeforeTax) * product.quantity; diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 6fb5cdb4..e3b586f1 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -50,6 +50,7 @@ function convertToInternational(number) { } const Checkout = () => { + const PPN = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0; const router = useRouter(); const query = router.query.source ?? null; const qVoucher = router.query.voucher ?? null; @@ -587,7 +588,6 @@ const Checkout = () => { setItemTnC(item); SetBottomPopupTnC(true); }; - // const taxTotal = (totalAmount - totalDiscountAmount - discountVoucher) * 0.11 const hasNoPrice = useMemo(() => { if (!products) return false; @@ -1210,7 +1210,7 @@ const Checkout = () => { <div>{currencyFormat(cartCheckout?.subtotal)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11%</div> + <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div> <div>{currencyFormat(cartCheckout?.tax)}</div> </div> <div className='flex gap-x-2 justify-between'> @@ -1514,7 +1514,7 @@ const Checkout = () => { <div>{currencyFormat(cartCheckout?.subtotal)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11%</div> + <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div> <div>{currencyFormat(cartCheckout?.tax)}</div> </div> <div className='flex gap-x-2 justify-between'> diff --git a/src/lib/checkout/components/CheckoutOld.jsx b/src/lib/checkout/components/CheckoutOld.jsx index 5b479a73..433c5672 100644 --- a/src/lib/checkout/components/CheckoutOld.jsx +++ b/src/lib/checkout/components/CheckoutOld.jsx @@ -239,7 +239,7 @@ const Checkout = () => { setIsLoading(false) window.location.href = payment.data.redirectUrl } - const taxTotal = (totalAmount - totalDiscountAmount) * 0.11 + const taxTotal = (totalAmount - totalDiscountAmount) * (PPN - 1) return ( <> diff --git a/src/lib/checkout/email/FinishCheckoutEmail.jsx b/src/lib/checkout/email/FinishCheckoutEmail.jsx index d19ba1ca..9a94587e 100644 --- a/src/lib/checkout/email/FinishCheckoutEmail.jsx +++ b/src/lib/checkout/email/FinishCheckoutEmail.jsx @@ -17,6 +17,7 @@ import { import FinishCheckout from '../components/FinishCheckout' const FinishCheckoutEmail = ({ transaction, payment, statusPayment }) => { + const PPN = process.env.NEXT_PUBLIC_PPN return ( <Html> @@ -206,9 +207,9 @@ const FinishCheckoutEmail = ({ transaction, payment, statusPayment }) => { </Column> </Row> <Row style={style.descriptionRow}> - <Column style={style.descriptionLCol}>PPN 11% (Incl.)</Column> + <Column style={style.descriptionLCol}>PPN {((PPN - 1) * 100).toFixed(0)}% (Incl.)</Column> <Column style={style.descriptionRCol}> - {currencyFormat(transaction.subtotal * 0.11)} + {currencyFormat(transaction.subtotal * (PPN - 1))} </Column> </Row> diff --git a/src/lib/flashSale/components/FlashSale.jsx b/src/lib/flashSale/components/FlashSale.jsx index f4be279e..9c0e713b 100644 --- a/src/lib/flashSale/components/FlashSale.jsx +++ b/src/lib/flashSale/components/FlashSale.jsx @@ -9,13 +9,23 @@ import { FlashSaleSkeleton } from '../skeleton/FlashSaleSkeleton'; const FlashSale = () => { const [flashSales, setFlashSales] = useState(null); const [isLoading, setIsLoading] = useState(true); + const [duration, setDuration] = useState(); + const calculateRemainingTime = (endDate) => { + const currentTime = new Date(); + const endTime = new Date(endDate); + const remainingTimeInSeconds = (endTime - currentTime) / 1000; + + return Math.max(Math.round(remainingTimeInSeconds), 0); + }; useEffect(() => { const loadFlashSales = async () => { const res = await fetch('/api/flashsale-header'); const { data } = await res.json(); if (data) { setFlashSales(data); + const remainingTime = calculateRemainingTime(data[0]?.endDate); + setDuration(remainingTime); } setIsLoading(false); }; @@ -37,7 +47,7 @@ const FlashSale = () => { <div className='font-medium sm:text-h-lg mt-1.5'> {flashSale.name} </div> - <CountDown initialTime={flashSale.duration} /> + <CountDown initialTime={duration} /> </div> <div className='relative'> @@ -59,7 +69,7 @@ const FlashSale = () => { /> <FlashSaleProduct flashSaleId={flashSale.pricelistId} - duration={flashSale.duration} + duration={duration} /> </div> </div> diff --git a/src/lib/form/components/KunjunganSales.jsx b/src/lib/form/components/KunjunganSales.jsx index ffa8f135..3779b836 100644 --- a/src/lib/form/components/KunjunganSales.jsx +++ b/src/lib/form/components/KunjunganSales.jsx @@ -1,17 +1,18 @@ -import odooApi from '@/core/api/odooApi' -import HookFormSelect from '@/core/components/elements/Select/HookFormSelect' -import cityApi from '@/lib/address/api/cityApi' -import { yupResolver } from '@hookform/resolvers/yup' -import React, { useEffect, useRef, useState } from 'react' -import ReCAPTCHA from 'react-google-recaptcha' -import { Controller, useForm } from 'react-hook-form' -import { toast } from 'react-hot-toast' -import * as Yup from 'yup' -import createLeadApi from '../api/createLeadApi' -import PageContent from '@/lib/content/components/PageContent' - -import useAuth from '@/core/hooks/useAuth' -import { useRouter } from 'next/router' +import odooApi from '@/core/api/odooApi'; +import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; +import cityApi from '@/lib/address/api/cityApi'; +import stateApi from '@/lib/address/api/stateApi.js'; +import { yupResolver } from '@hookform/resolvers/yup'; +import React, { useEffect, useRef, useState } from 'react'; +import ReCAPTCHA from 'react-google-recaptcha'; +import { Controller, useForm } from 'react-hook-form'; +import { toast } from 'react-hot-toast'; +import * as Yup from 'yup'; +import createLeadApi from '../api/createLeadApi'; +import PageContent from '@/lib/content/components/PageContent'; + +import useAuth from '@/core/hooks/useAuth'; +import { useRouter } from 'next/router'; const KunjunganSales = () => { const { @@ -19,44 +20,72 @@ const KunjunganSales = () => { handleSubmit, formState: { errors }, control, - reset + reset, + watch, + setValue, } = useForm({ resolver: yupResolver(validationSchema), - defaultValues - }) - const [cities, setCities] = useState([]) - const [companyTypes, setCompanyTypes] = useState([]) - const router = useRouter() + defaultValues, + }); + const [cities, setCities] = useState([]); + const [state, setState] = useState([]); + const [companyTypes, setCompanyTypes] = useState([]); + const router = useRouter(); + + const auth = useAuth(); - const auth = useAuth() + const recaptchaRef = useRef(null); - + if (auth == false) { + router.push(`/login?next=${encodeURIComponent('/kunjungan-sales')}`); + } + + useEffect(() => { + const loadState = async () => { + let dataState = await stateApi(); + dataState = dataState.map((state) => ({ + value: state.id, + label: state.name, + })); + setState(dataState); + }; + loadState(); + }, []); - const recaptchaRef = useRef(null) + const watchState = watch('state'); useEffect(() => { - if(auth == false) { - router.push('/login') + if (auth == false) { + return; } const loadCities = async () => { - let dataCities = await cityApi() - dataCities = dataCities.map((obj) => ({ value: obj.name, label: obj.name })) - setCities(dataCities) - } + setValue('city', ''); + let dataCities = await cityApi({ stateId: watchState }); + dataCities = dataCities?.map((obj) => ({ + value: obj.name, + label: obj.name, + })); + setCities(dataCities); + }; const loadCompanyTypes = async () => { - const dataCompanyTypes = await odooApi('GET', '/api/v1/partner/company_type') - setCompanyTypes(dataCompanyTypes?.map((obj) => ({ value: obj.name, label: obj.name }))) - } + const dataCompanyTypes = await odooApi( + 'GET', + '/api/v1/partner/company_type' + ); + setCompanyTypes( + dataCompanyTypes?.map((obj) => ({ value: obj.name, label: obj.name })) + ); + }; - loadCompanyTypes() - loadCities() - }, [auth]) + loadCompanyTypes(); + loadCities(); + }, [auth, watchState, setValue]); const onSubmitHandler = async (values) => { - const recaptchaValue = recaptchaRef.current.getValue() + const recaptchaValue = recaptchaRef.current.getValue(); if (!recaptchaValue) { - toast.error('Recaptcha harus diisi') - return + toast.error('Recaptcha harus diisi'); + return; } const data = { @@ -71,29 +100,37 @@ const KunjunganSales = () => { `Unit Perusahaan: ${values.companyType}`, `No. Handphone: ${values.mobile}`, `Alamat Email: ${values.email}`, - `Keterangan: ${values.description}` - ].join('\n') - } + `Keterangan: ${values.description}`, + ].join('\n'), + }; - const createLead = await createLeadApi({ data }) + const createLead = await createLeadApi({ data }); if (createLead) { - toast.success('Berhasil mengirimkan formulir kunjungan sales') - reset() - recaptchaRef.current.reset() + toast.success('Berhasil mengirimkan formulir kunjungan sales'); + reset(); + recaptchaRef.current.reset(); } + }; + if (!auth) { + return; } return ( <div className='container mx-auto p-4 md:p-0 my-0 md:my-10'> - <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Kunjungan Sales</h1> + <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'> + Kunjungan Sales + </h1> <div className='w-full grid grid-cols-1 md:grid-cols-2 gap-x-2'> - <form onSubmit={handleSubmit(onSubmitHandler)} className='grid grid-cols-1 gap-y-6'> + <form + onSubmit={handleSubmit(onSubmitHandler)} + className='grid grid-cols-1 gap-y-6' + > <div className='flex items-center bg-blue-100 border border-blue-300 text-blue-500 font-medium px-4 py-3 rounded leading-6' role='alert' > - Hubungi kami untuk mendapatkan kunjungan sales kami dan dapatkan berbagai kelebihannya - dengan menjadi pelanggan korporat kami. + Hubungi kami untuk mendapatkan kunjungan sales kami dan dapatkan + berbagai kelebihannya dengan menjadi pelanggan korporat kami. </div> <div> <label className='form-label mb-2'>Nama Perusahan*</label> @@ -104,7 +141,9 @@ const KunjunganSales = () => { className='form-input' aria-invalid={errors.company?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.company?.message} + </div> </div> <div> @@ -116,7 +155,9 @@ const KunjunganSales = () => { className='form-input' aria-invalid={errors.phone?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.phone?.message} + </div> </div> <div> @@ -128,7 +169,21 @@ const KunjunganSales = () => { className='form-input' aria-invalid={errors.address?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.address?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.address?.message} + </div> + </div> + + <div> + <label className='form-label mb-2'>Provinsi*</label> + <Controller + name='state' + control={control} + render={(props) => <HookFormSelect {...props} options={state} />} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.stateId?.message} + </div> </div> <div> @@ -138,7 +193,9 @@ const KunjunganSales = () => { control={control} render={(props) => <HookFormSelect {...props} options={cities} />} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.city?.message} + </div> </div> <div> @@ -146,9 +203,13 @@ const KunjunganSales = () => { <Controller name='companyType' control={control} - render={(props) => <HookFormSelect {...props} options={companyTypes} />} + render={(props) => ( + <HookFormSelect {...props} options={companyTypes} /> + )} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.companyType?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.companyType?.message} + </div> </div> <div> @@ -160,7 +221,9 @@ const KunjunganSales = () => { className='form-input' aria-invalid={errors.mobile?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.mobile?.message} + </div> </div> <div> @@ -172,34 +235,49 @@ const KunjunganSales = () => { className='form-input' aria-invalid={errors.email?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.email?.message} + </div> </div> <div> <label className='form-label mb-2'>Keterangan</label> - <textarea {...register('description')} type='text' className='form-input' /> + <textarea + {...register('description')} + type='text' + className='form-input' + /> </div> - <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} /> + <ReCAPTCHA + ref={recaptchaRef} + sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} + /> - <button type='submit' className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'> + <button + type='submit' + className='btn-yellow w-full md:w-fit ml-0 md:ml-auto' + > Simpan </button> </form> <PageContent path='/kunjungan-sales' /> </div> </div> - ) -} + ); +}; const validationSchema = Yup.object().shape({ - email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'), + email: Yup.string() + .email('Format harus seperti contoh@email.com') + .required('Harus di-isi'), company: Yup.string().required('Harus di-isi'), phone: Yup.string().required('Harus di-isi'), mobile: Yup.string().required('Harus di-isi'), address: Yup.string().required('Harus di-isi'), - city: Yup.string().required('Harus dipilih') -}) + city: Yup.string().required('Harus dipilih'), + state: Yup.string().required('Harus dipilih'), +}); const defaultValues = { email: '', @@ -208,8 +286,9 @@ const defaultValues = { mobile: '', address: '', city: '', + state: '', companyType: '', - description: '' -} + description: '', +}; -export default KunjunganSales +export default KunjunganSales; diff --git a/src/lib/form/components/KunjunganService.jsx b/src/lib/form/components/KunjunganService.jsx index 5720d14e..e3c83f78 100644 --- a/src/lib/form/components/KunjunganService.jsx +++ b/src/lib/form/components/KunjunganService.jsx @@ -1,16 +1,17 @@ -import HookFormSelect from '@/core/components/elements/Select/HookFormSelect' -import cityApi from '@/lib/address/api/cityApi' -import { yupResolver } from '@hookform/resolvers/yup' -import React, { useEffect, useRef, useState } from 'react' -import ReCAPTCHA from 'react-google-recaptcha' -import { Controller, useForm } from 'react-hook-form' -import { toast } from 'react-hot-toast' -import * as Yup from 'yup' -import createLeadApi from '../api/createLeadApi' -import PageContent from '@/lib/content/components/PageContent' -import { useRouter } from 'next/router' +import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; +import cityApi from '@/lib/address/api/cityApi'; +import stateApi from '@/lib/address/api/stateApi.js'; +import { yupResolver } from '@hookform/resolvers/yup'; +import React, { useEffect, useRef, useState } from 'react'; +import ReCAPTCHA from 'react-google-recaptcha'; +import { Controller, useForm } from 'react-hook-form'; +import { toast } from 'react-hot-toast'; +import * as Yup from 'yup'; +import createLeadApi from '../api/createLeadApi'; +import PageContent from '@/lib/content/components/PageContent'; +import { useRouter } from 'next/router'; -import useAuth from '@/core/hooks/useAuth' +import useAuth from '@/core/hooks/useAuth'; const CreateKunjunganService = () => { const { @@ -18,37 +19,61 @@ const CreateKunjunganService = () => { handleSubmit, formState: { errors }, control, - reset + reset, + watch, + setValue, } = useForm({ resolver: yupResolver(validationSchema), - defaultValues - }) - const [cities, setCities] = useState([]) - const [company_unit, setCompany_unit] = useState([]) - - const router = useRouter() + defaultValues, + }); + const [cities, setCities] = useState([]); + const [state, setState] = useState([]); + const [company_unit, setCompany_unit] = useState([]); - const auth = useAuth() + const router = useRouter(); - const recaptchaRef = useRef(null) + const auth = useAuth(); + if (auth == false) { + router.push(`/login?next=${encodeURIComponent('/kunjungan-service')}`); + } + + const recaptchaRef = useRef(null); + + useEffect(() => { + const loadState = async () => { + let dataState = await stateApi(); + dataState = dataState.map((state) => ({ + value: state.id, + label: state.name, + })); + setState(dataState); + }; + loadState(); + }, []); + + const watchState = watch('state'); useEffect(() => { - if(auth == false) { - router.push('/login') + if (auth == false) { + return; } const loadCities = async () => { - let dataCities = await cityApi() - dataCities = dataCities.map((city) => ({ value: city.id, label: city.name })) - setCities(dataCities) - } - loadCities() - }, [auth]) + setValue('city', ''); + let dataCities = await cityApi({ stateId: watchState }); + dataCities = dataCities.map((city) => ({ + value: city.id, + label: city.name, + })); + setCities(dataCities); + }; + loadCities(); + }, [auth, watchState, setValue]); const onSubmitHandler = async (values) => { - const recaptchaValue = recaptchaRef.current.getValue() + const recaptchaValue = recaptchaRef.current.getValue(); if (!recaptchaValue) { - toast.error('Catcha harus diisi') - return + toast.error('Catcha harus diisi'); + return; } const data = { ...values, @@ -70,164 +95,212 @@ const CreateKunjunganService = () => { ' \r\n Email : ' + values.email + ' \r\n Keterangan : ' + - values.description - } + values.description, + }; - const create_leads = await createLeadApi({ data }) + const create_leads = await createLeadApi({ data }); if (create_leads) { - toast.success('Berhasil menambahkan alamat') - reset() - recaptchaRef.current.reset() + toast.success('Berhasil menambahkan alamat'); + reset(); + recaptchaRef.current.reset(); } + }; + if (!auth) { + return; } return ( <div className='container mx-auto p-4 md:p-0 my-0 md:my-10'> - <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Kunjungan Service</h1> + <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'> + Kunjungan Service + </h1> <div className='w-full p-4 bg-white border border-gray_r-6 rounded'> <div className='flex items-center bg-blue-100 border border-blue-400 text-blue-500 font-bold px-4 py-3 mb-4' role='alert' > <p> - Tidak punya waktu untuk melakukan service atau perawatan rutin? Silahkan hubungi teknisi - kami untuk melakukan kunjungan ke tempat Anda di Jabodetabek. + Tidak punya waktu untuk melakukan service atau perawatan rutin? + Silahkan hubungi teknisi kami untuk melakukan kunjungan ke tempat + Anda di Jabodetabek. </p> </div> <div className='w-full grid grid-cols-2 gap-x-2'> - - <form onSubmit={handleSubmit(onSubmitHandler)}> - <div className=''> - <div> - <label className='form-label mb-2'>Nama Perusahan *</label> - <input - {...register('company')} - placeholder='PT.Indoteknik' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div> + <form onSubmit={handleSubmit(onSubmitHandler)}> + <div className=''> + <div> + <label className='form-label mb-2'>Nama Perusahan *</label> + <input + {...register('company')} + placeholder='PT.Indoteknik' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.company?.message} + </div> + </div> </div> - </div> - <div className=''> - <div> - <label className='form-label mb-2'>No. Telp *</label> - <input - {...register('phone')} - placeholder='021-XXXX' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div> + <div className=''> + <div> + <label className='form-label mb-2'>No. Telp *</label> + <input + {...register('phone')} + placeholder='021-XXXX' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.phone?.message} + </div> + </div> </div> - </div> - <div className=''> - <div> - <label className='form-label mb-2'>Alamat*</label> - <input - {...register('address')} - placeholder='jl. Bandengan no.31 ' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.address?.message}</div> + <div className=''> + <div> + <label className='form-label mb-2'>Alamat*</label> + <input + {...register('address')} + placeholder='jl. Bandengan no.31 ' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.address?.message} + </div> + </div> </div> - </div> - <div className=''> <div> - <label className='form-label mb-2'>Kota*</label> + <label className='form-label mb-2'>Provinsi*</label> <Controller - name='city' + name='state' control={control} - render={(props) => <HookFormSelect {...props} options={cities} />} + render={(props) => ( + <HookFormSelect {...props} options={state} /> + )} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.state?.message} + </div> </div> - </div> - <div className=''> - <div> - <label className='form-label mb-2'>Contact Person*</label> - <input - {...register('cp')} - placeholder='Jhone doe' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.cp?.message}</div> + <div className=''> + <div> + <label className='form-label mb-2'>Kota*</label> + <Controller + name='city' + control={control} + render={(props) => ( + <HookFormSelect {...props} options={cities} /> + )} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.city?.message} + </div> + </div> </div> - </div> - <div className=''> - <div> - <label className='form-label mb-2'>No HP *</label> - <input - {...register('mobile')} - placeholder='628XXXXXXX' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div> + <div className=''> + <div> + <label className='form-label mb-2'>Contact Person*</label> + <input + {...register('cp')} + placeholder='Jhone doe' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.cp?.message} + </div> + </div> </div> - </div> - <div className=''> - <div> - <label className='form-label mb-2'>Alamat Email *</label> - <input - {...register('email')} - placeholder='contoh@email.com' - type='email' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div> + <div className=''> + <div> + <label className='form-label mb-2'>No HP *</label> + <input + {...register('mobile')} + placeholder='628XXXXXXX' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.mobile?.message} + </div> + </div> </div> - </div> - <div className=''> - <div> - <label className='form-label mb-2'> - Sebutkan: Merek, Tipe, Permasalahan, Service, Perawatan - </label> - <textarea {...register('description')} type='text' className='form-input' /> - <div className='text-caption-2 text-danger-500 mt-1'> - {errors.description?.message} + <div className=''> + <div> + <label className='form-label mb-2'>Alamat Email *</label> + <input + {...register('email')} + placeholder='contoh@email.com' + type='email' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.email?.message} + </div> </div> </div> - </div> - <div className=''> - <div> - <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} /> + <div className=''> + <div> + <label className='form-label mb-2'> + Sebutkan: Merek, Tipe, Permasalahan, Service, Perawatan + </label> + <textarea + {...register('description')} + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.description?.message} + </div> + </div> </div> - </div> - <div className=''> - <div> - <button type='submit' className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'> - Simpan - </button> + <div className=''> + <div> + <ReCAPTCHA + ref={recaptchaRef} + sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} + /> + </div> + </div> + <div className=''> + <div> + <button + type='submit' + className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto' + > + Simpan + </button> + </div> </div> - </div> - </form> - <PageContent path='/kunjungan-service' /> + </form> + <PageContent path='/kunjungan-service' /> </div> </div> </div> - ) -} + ); +}; const validationSchema = Yup.object().shape({ company: Yup.string().required('Harus di-isi'), - email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'), + email: Yup.string() + .email('Format harus seperti contoh@email.com') + .required('Harus di-isi'), phone: Yup.string().required('Harus di-isi'), city: Yup.string().required('Harus di-isi'), + state: Yup.string().required('Harus dipilih'), cp: Yup.string().required('Harus di-isi'), mobile: Yup.string().required('Harus di-isi'), email: Yup.string().required('Harus di-isi'), - address: Yup.string().required('Harus di-isi') -}) + address: Yup.string().required('Harus di-isi'), +}); const defaultValues = { company: '', email: '', phone: '', city: '', + state: '', cp: '', mobile: '', email: '', - address: '' -} + address: '', +}; -export default CreateKunjunganService +export default CreateKunjunganService; diff --git a/src/lib/form/components/Merchant.jsx b/src/lib/form/components/Merchant.jsx index 85f72bf8..ee7d177d 100644 --- a/src/lib/form/components/Merchant.jsx +++ b/src/lib/form/components/Merchant.jsx @@ -1,5 +1,6 @@ import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; import cityApi from '@/lib/address/api/cityApi'; +import stateApi from '@/lib/address/api/stateApi.js'; import { yupResolver } from '@hookform/resolvers/yup'; import React, { useEffect, useRef, useState } from 'react'; import ReCAPTCHA from 'react-google-recaptcha'; @@ -9,8 +10,7 @@ import * as Yup from 'yup'; import createLeadApi from '../api/createLeadApi'; import PageContent from '@/lib/content/components/PageContent'; import { useRouter } from 'next/router'; -import useAuth from '@/core/hooks/useAuth' - +import useAuth from '@/core/hooks/useAuth'; const CreateMerchant = () => { const { @@ -19,6 +19,8 @@ const CreateMerchant = () => { formState: { errors }, control, reset, + watch, + setValue, } = useForm({ resolver: yupResolver(validationSchema), defaultValues, @@ -50,27 +52,45 @@ const CreateMerchant = () => { }, ]; const [cities, setCities] = useState([]); + const [state, setState] = useState([]); const [company_unit, setCompany_unit] = useState(list_unit); const recaptchaRef = useRef(null); - const router = useRouter() + const router = useRouter(); + + const auth = useAuth(); + if (auth == false) { + router.push(`/login?next=${encodeURIComponent('/daftar-merchant')}`); + } - const auth = useAuth() + useEffect(() => { + const loadState = async () => { + let dataState = await stateApi(); + dataState = dataState.map((state) => ({ + value: state.id, + label: state.name, + })); + setState(dataState); + }; + loadState(); + }, []); + const watchState = watch('state'); useEffect(() => { - if(auth == false) { - router.push('/login') + if (auth == false) { + return; } const loadCities = async () => { - let dataCities = await cityApi(); - dataCities = dataCities.map((city) => ({ + setValue('city', ''); + let dataCities = await cityApi({ stateId: watchState }); + dataCities = dataCities?.map((city) => ({ value: city.id, label: city.name, })); setCities(dataCities); }; loadCities(); - }, [auth]); + }, [auth, watchState, setValue]); const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue(); @@ -111,6 +131,9 @@ const CreateMerchant = () => { recaptchaRef.current.reset(); } }; + if (!auth) { + return; + } return ( <div className='container mx-auto p-4 md:p-0 my-0 md:my-10'> <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'> @@ -172,6 +195,19 @@ const CreateMerchant = () => { </div> </div> </div> + <div> + <label className='form-label mb-2'>Provinsi*</label> + <Controller + name='state' + control={control} + render={(props) => ( + <HookFormSelect {...props} options={state} /> + )} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.state?.message} + </div> + </div> <div className=''> <div> <label className='form-label mb-2'>Kota*</label> @@ -290,6 +326,7 @@ const validationSchema = Yup.object().shape({ .required('Harus di-isi'), phone: Yup.string().required('Harus di-isi'), cp: Yup.string().required('Harus di-isi'), + state: Yup.string().required('Harus dipilih'), city: Yup.string().required('Harus di-isi'), company_unit: Yup.string().required('Harus di-isi'), address: Yup.string().required('Harus di-isi'), @@ -300,6 +337,7 @@ const defaultValues = { company: '', email: '', phone: '', + state: '', city: '', company_unit: '', cp: '', diff --git a/src/lib/form/components/RequestForQuotation.jsx b/src/lib/form/components/RequestForQuotation.jsx index 68b7fa17..170a5c62 100644 --- a/src/lib/form/components/RequestForQuotation.jsx +++ b/src/lib/form/components/RequestForQuotation.jsx @@ -1,18 +1,19 @@ -import odooApi from '@/core/api/odooApi' -import HookFormSelect from '@/core/components/elements/Select/HookFormSelect' -import cityApi from '@/lib/address/api/cityApi' -import { yupResolver } from '@hookform/resolvers/yup' -import React, { useEffect, useRef, useState } from 'react' -import ReCAPTCHA from 'react-google-recaptcha' -import { Controller, useForm } from 'react-hook-form' -import { toast } from 'react-hot-toast' -import * as Yup from 'yup' -import createLeadApi from '../api/createLeadApi' -import getFileBase64 from '@/core/utils/getFileBase64' -import PageContent from '@/lib/content/components/PageContent' -import { useRouter } from 'next/router' - -import useAuth from '@/core/hooks/useAuth' +import odooApi from '@/core/api/odooApi'; +import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; +import cityApi from '@/lib/address/api/cityApi'; +import stateApi from '@/lib/address/api/stateApi.js'; +import { yupResolver } from '@hookform/resolvers/yup'; +import React, { useEffect, useRef, useState } from 'react'; +import ReCAPTCHA from 'react-google-recaptcha'; +import { Controller, useForm } from 'react-hook-form'; +import { toast } from 'react-hot-toast'; +import * as Yup from 'yup'; +import createLeadApi from '../api/createLeadApi'; +import getFileBase64 from '@/core/utils/getFileBase64'; +import PageContent from '@/lib/content/components/PageContent'; +import { useRouter } from 'next/router'; + +import useAuth from '@/core/hooks/useAuth'; const RequestForQuotation = () => { const { @@ -20,47 +21,69 @@ const RequestForQuotation = () => { handleSubmit, formState: { errors }, control, - reset + reset, + watch, + setValue, } = useForm({ resolver: yupResolver(validationSchema), - defaultValues - }) - const [cities, setCities] = useState([]) + defaultValues, + }); + const [cities, setCities] = useState([]); + const [state, setState] = useState([]); - const quotationFileRef = useRef(null) - const recaptchaRef = useRef(null) - const router = useRouter() + const quotationFileRef = useRef(null); + const recaptchaRef = useRef(null); + const router = useRouter(); - const auth = useAuth() + const auth = useAuth(); + if (auth == false) { + router.push(`/login?next=${encodeURIComponent('/request-for-quotation')}`); + } + useEffect(() => { + const loadState = async () => { + let dataState = await stateApi(); + dataState = dataState.map((state) => ({ + value: state.id, + label: state.name, + })); + setState(dataState); + }; + loadState(); + }, []); + const watchState = watch('state'); useEffect(() => { - if(auth == false) { - router.push('/login') + if (!auth) { + return; } const loadCities = async () => { - let dataCities = await cityApi() - dataCities = dataCities.map((obj) => ({ value: obj.name, label: obj.name })) - setCities(dataCities) - } - loadCities() - }, [auth]) + setValue('city', ''); + let dataCities = await cityApi({ stateId: watchState }); + dataCities = dataCities?.map((obj) => ({ + value: obj.name, + label: obj.name, + })); + setCities(dataCities); + }; + loadCities(); + }, [auth, watchState, setValue]); const onSubmitHandler = async (values) => { - const recaptchaValue = recaptchaRef.current.getValue() + const recaptchaValue = recaptchaRef.current.getValue(); if (!recaptchaValue) { - toast.error('Recaptcha harus diisi') - return + toast.error('Recaptcha harus diisi'); + return; } - const file = quotationFileRef.current.files[0] - let fileBase64 = null + const file = quotationFileRef.current.files[0]; + let fileBase64 = null; if (typeof file !== 'undefined') { if (file.size > 5000000) { - toast.error('Maksimal ukuran file adalah 5MB') - return + toast.error('Maksimal ukuran file adalah 5MB'); + return; } - fileBase64 = await getFileBase64(file) + fileBase64 = await getFileBase64(file); } const data = { @@ -73,33 +96,42 @@ const RequestForQuotation = () => { `Kota: ${values.city}`, `No. Handphone: ${values.mobile}`, `Alamat Email: ${values.email}`, - `Keterangan: ${values.description}` - ].join('\n') - } + `Keterangan: ${values.description}`, + ].join('\n'), + }; - if (fileBase64) data.file_quotation = fileBase64 + if (fileBase64) data.file_quotation = fileBase64; - const createLead = await createLeadApi({ data }) + const createLead = await createLeadApi({ data }); if (createLead) { - toast.success('Berhasil mengirimkan formulir request for quotation') - reset() - recaptchaRef.current.reset() + toast.success('Berhasil mengirimkan formulir request for quotation'); + reset(); + recaptchaRef.current.reset(); } + }; + if (!auth) { + return; } return ( <div className='container mx-auto p-4 md:p-0 my-0 md:my-10'> - <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Request for Quotation</h1> + <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'> + Request for Quotation + </h1> <div className='w-full grid grid-cols-1 md:grid-cols-2 gap-x-2'> - <form onSubmit={handleSubmit(onSubmitHandler)} className='grid grid-cols-1 gap-y-6'> + <form + onSubmit={handleSubmit(onSubmitHandler)} + className='grid grid-cols-1 gap-y-6' + > <div className='flex items-center bg-blue-100 border border-blue-300 text-blue-500 font-medium px-4 py-3 rounded leading-6' role='alert' > - Halaman untuk pengajuan penawaran harga, lengkapi data di bawah ini dengan jelas untuk - mempermudah tim support kami melayani kebutuhan Anda. Tim kami akan sesegera mungkin - untuk membuatkan penawaran harga terbaik, hubungi kami melalui telpon jika ada - keterlambatan pelayanan. + Halaman untuk pengajuan penawaran harga, lengkapi data di bawah ini + dengan jelas untuk mempermudah tim support kami melayani kebutuhan + Anda. Tim kami akan sesegera mungkin untuk membuatkan penawaran + harga terbaik, hubungi kami melalui telpon jika ada keterlambatan + pelayanan. </div> <div> <label className='form-label mb-2'>Nama Perusahan*</label> @@ -110,7 +142,9 @@ const RequestForQuotation = () => { className='form-input' aria-invalid={errors.company?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.company?.message} + </div> </div> <div> @@ -122,7 +156,21 @@ const RequestForQuotation = () => { className='form-input' aria-invalid={errors.phone?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.phone?.message} + </div> + </div> + + <div> + <label className='form-label mb-2'>Provinsi*</label> + <Controller + name='state' + control={control} + render={(props) => <HookFormSelect {...props} options={state} />} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.state?.message} + </div> </div> <div> @@ -132,7 +180,9 @@ const RequestForQuotation = () => { control={control} render={(props) => <HookFormSelect {...props} options={cities} />} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.city?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.city?.message} + </div> </div> <div> @@ -144,7 +194,9 @@ const RequestForQuotation = () => { className='form-input' aria-invalid={errors.contactPerson?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.contactPerson?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.contactPerson?.message} + </div> </div> <div> @@ -156,7 +208,9 @@ const RequestForQuotation = () => { className='form-input' aria-invalid={errors.mobile?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.mobile?.message} + </div> </div> <div> @@ -168,39 +222,59 @@ const RequestForQuotation = () => { className='form-input' aria-invalid={errors.email?.message} /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.email?.message} + </div> </div> <div> <label className='form-label mb-2'>Keterangan</label> - <textarea {...register('description')} type='text' className='form-input' /> + <textarea + {...register('description')} + type='text' + className='form-input' + /> </div> <div> <label className='form-label mb-2'>File Daftar Produk</label> - <input type="file" name="quotationFile" className='form-input' ref={quotationFileRef} /> + <input + type='file' + name='quotationFile' + className='form-input' + ref={quotationFileRef} + /> </div> - <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} /> + <ReCAPTCHA + ref={recaptchaRef} + sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} + /> - <button type='submit' className='btn-yellow w-full md:w-fit ml-0 md:ml-auto'> + <button + type='submit' + className='btn-yellow w-full md:w-fit ml-0 md:ml-auto' + > Simpan </button> </form> <PageContent path='/request-for-quotation' /> </div> </div> - ) -} + ); +}; const validationSchema = Yup.object().shape({ - email: Yup.string().email('Format harus seperti contoh@email.com').required('Harus di-isi'), + email: Yup.string() + .email('Format harus seperti contoh@email.com') + .required('Harus di-isi'), company: Yup.string().required('Harus di-isi'), phone: Yup.string().required('Harus di-isi'), mobile: Yup.string().required('Harus di-isi'), city: Yup.string().required('Harus dipilih'), + state: Yup.string().required('Harus dipilih'), contactPerson: Yup.string().required('Harus dipilih'), -}) +}); const defaultValues = { email: '', @@ -209,7 +283,8 @@ const defaultValues = { mobile: '', address: '', city: '', - description: '' -} + state: '', + description: '', +}; -export default RequestForQuotation +export default RequestForQuotation; diff --git a/src/lib/form/components/SuratDukungan.jsx b/src/lib/form/components/SuratDukungan.jsx index 31e7ee83..fadb2c57 100644 --- a/src/lib/form/components/SuratDukungan.jsx +++ b/src/lib/form/components/SuratDukungan.jsx @@ -1,5 +1,6 @@ import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; import cityApi from '@/lib/address/api/cityApi'; +import stateApi from '@/lib/address/api/stateApi.js'; import { yupResolver } from '@hookform/resolvers/yup'; import React, { useEffect, useRef, useState } from 'react'; import ReCAPTCHA from 'react-google-recaptcha'; @@ -10,7 +11,7 @@ import createLeadsApi from '../api/createLeadApi'; import PageContent from '@/lib/content/components/PageContent'; -import useAuth from '@/core/hooks/useAuth' +import useAuth from '@/core/hooks/useAuth'; import { useRouter } from 'next/router'; const CreateSuratDukungan = () => { @@ -20,32 +21,21 @@ const CreateSuratDukungan = () => { formState: { errors }, control, reset, + watch, + setValue, } = useForm({ resolver: yupResolver(validationSchema), defaultValues, }); - const [cities, setCities] = useState([]); const [company_unit, setCompany_unit] = useState([]); const recaptchaRef = useRef(null); - const router = useRouter() + const router = useRouter(); - const auth = useAuth() - - useEffect(() => { - if(auth == false) { - router.push('/login') - } - const loadCities = async () => { - let dataCities = await cityApi(); - dataCities = dataCities.map((city) => ({ - value: city.id, - label: city.name, - })); - setCities(dataCities); - }; - loadCities(); - }, [auth]); + const auth = useAuth(); + if (auth == false) { + router.push(`/login?next=${encodeURIComponent('/surat-dukungan')}`); + } const onSubmitHandler = async (values) => { const recaptchaValue = recaptchaRef.current.getValue(); @@ -85,6 +75,9 @@ const CreateSuratDukungan = () => { recaptchaRef.current.reset(); } }; + if (!auth) { + return; + } return ( <div className='container mx-auto p-4 md:p-0 my-0 md:my-10'> <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'> @@ -241,7 +234,7 @@ const CreateSuratDukungan = () => { </div> </form> </div> - <PageContent path='/surat-dukungan'/> + <PageContent path='/surat-dukungan' /> </div> </div> </div> diff --git a/src/lib/home/components/ServiceList.jsx b/src/lib/home/components/ServiceList.jsx index 6d03a587..e32e8747 100644 --- a/src/lib/home/components/ServiceList.jsx +++ b/src/lib/home/components/ServiceList.jsx @@ -59,7 +59,7 @@ const ServiceList = () => { </div> <div className='w-full '> <Link - href='/pembayaran-tempo' + href='/pembayaran-tempo-detail' className='border border-gray-200 p-2 flex items-center gap-x-2 rounded-lg' > <div className=''> diff --git a/src/lib/invoice/components/Invoice.jsx b/src/lib/invoice/components/Invoice.jsx index 81202b1c..15bfa746 100644 --- a/src/lib/invoice/components/Invoice.jsx +++ b/src/lib/invoice/components/Invoice.jsx @@ -13,6 +13,7 @@ import { createSlug } from '@/core/utils/slug' import { useEffect, useState } from 'react' const Invoice = ({ id }) => { + const PPN = process.env.NEXT_PUBLIC_PPN const { invoice } = useInvoice({ id }) const [totalAmount, setTotalAmount] = useState(0) @@ -255,8 +256,8 @@ const Invoice = ({ id }) => { {currencyFormat(invoice.data?.amountTotal)} </div> - <div className='text-right'>PPN 11% (Incl.)</div> - <div className='text-right font-medium'>{currencyFormat(totalAmount * 0.11)}</div> + <div className='text-right'>PPN {((PPN - 1) * 100).toFixed(0)}% (Incl.)</div> + <div className='text-right font-medium'>{currencyFormat(invoice.data?.amountTotal - totalAmount)}</div> </div> </div> </div> diff --git a/src/lib/quotation/components/Quotation.jsx b/src/lib/quotation/components/Quotation.jsx index 5a2f63a5..2f4d6c46 100644 --- a/src/lib/quotation/components/Quotation.jsx +++ b/src/lib/quotation/components/Quotation.jsx @@ -37,6 +37,7 @@ const { checkoutApi } = require('@/lib/checkout/api/checkoutApi'); const { getProductsCheckout } = require('@/lib/checkout/api/checkoutApi'); const Quotation = () => { + const PPN = process.env.NEXT_PUBLIC_PPN ? parseFloat(process.env.NEXT_PUBLIC_PPN) : 0; const router = useRouter(); const auth = useAuth(); const query = router.query.source ?? null; @@ -307,7 +308,7 @@ const Quotation = () => { toast.error('Gagal melakukan transaksi, terjadi kesalahan internal'); }; - const taxTotal = (totalAmount - totalDiscountAmount) * 0.11; + const taxTotal = (totalAmount - totalDiscountAmount) * (PPN - 1); return ( <> @@ -425,7 +426,7 @@ const Quotation = () => { <div>{currencyFormat(cartCheckout?.subtotal)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11%</div> + <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div> <div>{currencyFormat(cartCheckout?.tax)}</div> </div> <div className='flex gap-x-2 justify-between'> @@ -574,7 +575,7 @@ const Quotation = () => { <div>{currencyFormat(cartCheckout?.subtotal)}</div> </div> <div className='flex gap-x-2 justify-between'> - <div className='text-gray_r-11'>PPN 11%</div> + <div className='text-gray_r-11'>PPN {((PPN - 1) * 100).toFixed(0)}%</div> <div>{currencyFormat(cartCheckout?.tax)}</div> </div> <div className='flex gap-x-2 justify-between'> diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index f5dc507a..b2fb2c17 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -42,6 +42,7 @@ import { gtagPurchase } from '@/core/utils/googleTag'; import { deleteItemCart } from '@/core/utils/cart'; import axios from 'axios'; const Transaction = ({ id }) => { + const PPN = process.env.NEXT_PUBLIC_PPN; const router = useRouter(); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedProduct, setSelectedProduct] = useState(null); @@ -207,7 +208,7 @@ const Transaction = ({ id }) => { <p>{currencyFormat(transaction.data?.amountUntaxed)}</p> </div> <div className='flex justify-between mt-1'> - <p className='text-gray_r-12/70'>PPN 11%</p> + <p className='text-gray_r-12/70'>PPN {((PPN - 1) * 100).toFixed(0)}%</p> <p>{currencyFormat(transaction.data?.amountTax)}</p> </div> <div className='flex justify-between mt-1'> @@ -975,7 +976,7 @@ const Transaction = ({ id }) => { {currencyFormat(transaction.data?.amountUntaxed)} </div> - <div className='text-right'>PPN 11%</div> + <div className='text-right'>PPN {((PPN - 1) * 100).toFixed(0)}%</div> <div className='text-right font-medium'> {currencyFormat(transaction.data?.amountTax)} </div> diff --git a/src/pages/api/flashsale-header.js b/src/pages/api/flashsale-header.js index 21cb9c9d..578801ae 100644 --- a/src/pages/api/flashsale-header.js +++ b/src/pages/api/flashsale-header.js @@ -20,13 +20,19 @@ export default async function handler(req, res) { let cachedData = await client.get(cacheKey); if (cachedData) { - const data = JSON.parse(cachedData); + const data = JSON.parse(cachedData); // Periksa apakah data adalah array dan panjangnya 0 if (!data || (Array.isArray(data) && data.length === 0)) { await client.del(cacheKey); // Hapus kunci jika data kosong return res.status(200).json({ data: [] }); } + // Periksa apakah end_date lebih besar dari waktu saat ini + const currentTime = new Date(); + if (data[0].endDate && new Date(data[0].endDate) < currentTime) { + await client.del(cacheKey); // Hapus kunci jika end_date lebih kecil dari waktu saat ini + return res.status(200).json({ data: [] }); + } return res.status(200).json({ data }); } else { const flashSale = await odooApi('GET', `/api/v1/flashsale/header`); @@ -37,11 +43,11 @@ export default async function handler(req, res) { cacheKey, JSON.stringify(flashSale), 'EX', - flashSale.duration + flashSale.duration ); cachedData = await client.get(cacheKey); - const data = JSON.parse(cachedData); + const data = JSON.parse(cachedData); return res.status(200).json({ data }); } } diff --git a/src/pages/api/shop/midtrans-payment.js b/src/pages/api/shop/midtrans-payment.js index 12aaa51f..f90e9e81 100644 --- a/src/pages/api/shop/midtrans-payment.js +++ b/src/pages/api/shop/midtrans-payment.js @@ -3,6 +3,7 @@ import camelcaseObjectDeep from 'camelcase-object-deep' import midtransClient from 'midtrans-client' export default async function handler(req, res) { + const PPN = process.env.NEXT_PUBLIC_PPN const { transactionId = null } = req.query if (!transactionId) { diff --git a/src/pages/my/daftar-merchant.jsx b/src/pages/daftar-merchant.jsx index e1fa9bcb..e1fa9bcb 100644 --- a/src/pages/my/daftar-merchant.jsx +++ b/src/pages/daftar-merchant.jsx diff --git a/src/pages/google_merchant/products/[page].js b/src/pages/google_merchant/products/[page].js index 0c2cf3c5..8395f839 100644 --- a/src/pages/google_merchant/products/[page].js +++ b/src/pages/google_merchant/products/[page].js @@ -6,6 +6,7 @@ import _ from 'lodash-contrib'; import { create } from 'xmlbuilder'; export async function getServerSideProps({ res, query }) { + const PPN = process.env.NEXT_PUBLIC_PPN const titleContent = 'Indoteknik.com: B2B Industrial Supply & Solution'; const descriptionContent = 'Temukan pilihan produk B2B Industri & Alat Teknik untuk Perusahaan, UMKM & Pemerintah dengan lengkap, mudah dan transparan.'; @@ -77,7 +78,7 @@ export async function getServerSideProps({ res, query }) { 'g:availability': { '#text': availability }, 'g:brand': { '#text': product.manufacture?.name || '' }, 'g:price': { - '#text': `${Math.round(product.lowestPrice.price * 1.11)} IDR`, + '#text': `${Math.round(product.lowestPrice.price * PPN)} IDR`, }, }; @@ -93,7 +94,7 @@ export async function getServerSideProps({ res, query }) { if (product.lowestPrice.discountPercentage > 0) { item['g:sale_price'] = { - '#text': `${Math.round(product.lowestPrice.priceDiscount * 1.11)} IDR`, + '#text': `${Math.round(product.lowestPrice.priceDiscount * PPN)} IDR`, }; } productItems.push(item); diff --git a/src/pages/my/kunjungan-sales.jsx b/src/pages/kunjungan-sales.jsx index 052991d9..052991d9 100644 --- a/src/pages/my/kunjungan-sales.jsx +++ b/src/pages/kunjungan-sales.jsx diff --git a/src/pages/my/kunjungan-service.jsx b/src/pages/kunjungan-service.jsx index 37de5a0b..37de5a0b 100644 --- a/src/pages/my/kunjungan-service.jsx +++ b/src/pages/kunjungan-service.jsx diff --git a/src/pages/my/pembayaran-tempo.jsx b/src/pages/my/pembayaran-tempo.jsx deleted file mode 100644 index 8947bdd9..00000000 --- a/src/pages/my/pembayaran-tempo.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import Seo from '@/core/components/Seo' -import BasicLayout from '@/core/components/layouts/BasicLayout' -import PembayaranTempo from '@/lib/form/components/PembayaranTempo' - -export default function pembayaranTempo() { - return ( - <> - <Seo title='Pembayaran Tempo - Indoteknik.com' /> - - <BasicLayout> - <PembayaranTempo /> - </BasicLayout> - </> - ) -} diff --git a/src/pages/pembayaran-tempo-detail.jsx b/src/pages/pembayaran-tempo-detail.jsx new file mode 100644 index 00000000..363e3099 --- /dev/null +++ b/src/pages/pembayaran-tempo-detail.jsx @@ -0,0 +1,13 @@ +import Seo from '@/core/components/Seo' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import IframeContent from '@/lib/iframe/components/IframeContent' + +export default function PembnayaranTempo() { + return ( + <BasicLayout> + <Seo title='Pambayaran Tempo - Indoteknik.com' /> + + <IframeContent url={`${process.env.NEXT_PUBLIC_ODOO_HOST}/content?url=pembayaran-tempo`} /> + </BasicLayout> + ) +} diff --git a/src/pages/pembayaran-tempo.jsx b/src/pages/pembayaran-tempo.jsx index 363e3099..8947bdd9 100644 --- a/src/pages/pembayaran-tempo.jsx +++ b/src/pages/pembayaran-tempo.jsx @@ -1,13 +1,15 @@ import Seo from '@/core/components/Seo' import BasicLayout from '@/core/components/layouts/BasicLayout' -import IframeContent from '@/lib/iframe/components/IframeContent' +import PembayaranTempo from '@/lib/form/components/PembayaranTempo' -export default function PembnayaranTempo() { +export default function pembayaranTempo() { return ( - <BasicLayout> - <Seo title='Pambayaran Tempo - Indoteknik.com' /> + <> + <Seo title='Pembayaran Tempo - Indoteknik.com' /> - <IframeContent url={`${process.env.NEXT_PUBLIC_ODOO_HOST}/content?url=pembayaran-tempo`} /> - </BasicLayout> + <BasicLayout> + <PembayaranTempo /> + </BasicLayout> + </> ) } diff --git a/src/pages/my/request-for-quotation.jsx b/src/pages/request-for-quotation.jsx index 40fda009..40fda009 100644 --- a/src/pages/my/request-for-quotation.jsx +++ b/src/pages/request-for-quotation.jsx diff --git a/src/pages/sitemap/homepage.xml.js b/src/pages/sitemap/homepage.xml.js index 08c52112..fa622d5c 100644 --- a/src/pages/sitemap/homepage.xml.js +++ b/src/pages/sitemap/homepage.xml.js @@ -5,29 +5,29 @@ export async function getServerSideProps({ res }) { { label: 'Hubungi Kami', url: 'https://indoteknik.com/hubungi-kami' }, { label: 'Tentang Kami', url: 'https://indoteknik.com/tentang-kami' }, { label: 'Karir', url: 'https://indoteknik.com/karir' }, - { label: 'Daftar Tempo', url: 'https://indoteknik.com/my/pembayaran-tempo' }, + { label: 'Daftar Tempo', url: 'https://indoteknik.com/pembayaran-tempo' }, ]; const sitemap = create('urlset', { encoding: 'utf-8' }).att( 'xmlns', 'http://www.sitemaps.org/schemas/sitemap/0.9' - ) + ); - const date = new Date() + const date = new Date(); links.forEach((link) => { - const url = sitemap.ele('url') - url.ele('loc', link.url) - url.ele('lastmod', date.toISOString().slice(0, 10)) - url.ele('changefreq', 'daily') - url.ele('priority', '1.0') - }) + const url = sitemap.ele('url'); + url.ele('loc', link.url); + url.ele('lastmod', date.toISOString().slice(0, 10)); + url.ele('changefreq', 'daily'); + url.ele('priority', '1.0'); + }); - res.setHeader('Content-Type', 'text/xml') - res.write(sitemap.end()) - res.end() + res.setHeader('Content-Type', 'text/xml'); + res.write(sitemap.end()); + res.end(); - return { props: {} } + return { props: {} }; } export default function SitemapProducts() { - return null + return null; } diff --git a/src/pages/my/surat-dukungan.jsx b/src/pages/surat-dukungan.jsx index 8058f34d..8058f34d 100644 --- a/src/pages/my/surat-dukungan.jsx +++ b/src/pages/surat-dukungan.jsx |
