diff options
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/auth/components/RegisterDesktop.jsx | 2 | ||||
| -rw-r--r-- | src/lib/auth/components/RegisterMobile.jsx | 2 | ||||
| -rw-r--r-- | src/lib/cart/components/Cart.jsx | 4 | ||||
| -rw-r--r-- | src/lib/form/components/KunjunganSales.jsx | 2 | ||||
| -rw-r--r-- | src/lib/form/components/KunjunganService.jsx | 2 | ||||
| -rw-r--r-- | src/lib/form/components/Merchant.jsx | 2 | ||||
| -rw-r--r-- | src/lib/form/components/RequestForQuotation.jsx | 203 | ||||
| -rw-r--r-- | src/lib/form/components/SuratDukungan.jsx | 2 | ||||
| -rw-r--r-- | src/lib/product/components/ProductCard.jsx | 1 | ||||
| -rw-r--r-- | src/lib/product/components/ProductSearch.jsx | 4 | ||||
| -rw-r--r-- | src/lib/review/api/customerReviewsApi.js | 6 | ||||
| -rw-r--r-- | src/lib/review/components/CustomerReviews.jsx | 62 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transaction.jsx | 4 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transactions.jsx | 10 |
14 files changed, 291 insertions, 15 deletions
diff --git a/src/lib/auth/components/RegisterDesktop.jsx b/src/lib/auth/components/RegisterDesktop.jsx index f624fba7..93b505ab 100644 --- a/src/lib/auth/components/RegisterDesktop.jsx +++ b/src/lib/auth/components/RegisterDesktop.jsx @@ -92,7 +92,7 @@ const RegisterDesktop = () => { /> </div> <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} /> - <div class='flex items-center mt-4 '> + <div className='flex items-center mt-4 '> <input type='checkbox' id='sdk' diff --git a/src/lib/auth/components/RegisterMobile.jsx b/src/lib/auth/components/RegisterMobile.jsx index 80ea6ab0..da6efaf5 100644 --- a/src/lib/auth/components/RegisterMobile.jsx +++ b/src/lib/auth/components/RegisterMobile.jsx @@ -96,7 +96,7 @@ const RegisterMobile = () => { /> </div> <ReCAPTCHA ref={recaptchaRef} sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_GOOGLE} /> - <div class='flex items-center mt-4 '> + <div className='flex items-center mt-4 '> <input type='checkbox' id='sdk' diff --git a/src/lib/cart/components/Cart.jsx b/src/lib/cart/components/Cart.jsx index 1008bffc..907d1267 100644 --- a/src/lib/cart/components/Cart.jsx +++ b/src/lib/cart/components/Cart.jsx @@ -76,7 +76,7 @@ const Cart = () => { const LoadProductSimilar = async () => { const randProductIndex = Math.floor(Math.random() * products.length) const productLoad = await productSearchApi({ - query: `q=${products?.[randProductIndex].parent.name}&limit=10` + query: `q=${products?.[randProductIndex].parent.name}&limit=10&operation=OR` }) setProductRecomendation(productLoad) @@ -474,7 +474,7 @@ const Cart = () => { </div> <div className='col-span-9 pt-2 pb-6 mt-6'> - <h1 className='text-title-sm font-semibold mb-6'>Product Yang Mungkin Kamu Suka</h1> + <h1 className='text-title-sm font-semibold mb-6'>Produk Yang Mungkin Kamu Suka</h1> <div className='grid grid-cols-5 gap-x-3 gap-y-6'> {productRecomendation && productRecomendation.response.products.map((product) => ( diff --git a/src/lib/form/components/KunjunganSales.jsx b/src/lib/form/components/KunjunganSales.jsx index 38a07345..7064f807 100644 --- a/src/lib/form/components/KunjunganSales.jsx +++ b/src/lib/form/components/KunjunganSales.jsx @@ -77,7 +77,7 @@ const KunjunganSales = () => { <div className='w-full grid grid-cols-1 md:grid-cols-2'> <form onSubmit={handleSubmit(onSubmitHandler)} className='grid grid-cols-1 gap-y-6'> <div - class='flex items-center bg-blue-100 border border-blue-300 text-blue-500 font-medium px-4 py-3 rounded leading-6' + 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 diff --git a/src/lib/form/components/KunjunganService.jsx b/src/lib/form/components/KunjunganService.jsx index 076f6814..965fbc89 100644 --- a/src/lib/form/components/KunjunganService.jsx +++ b/src/lib/form/components/KunjunganService.jsx @@ -74,7 +74,7 @@ const CreateKunjunganService = () => { <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 - class='flex items-center bg-blue-100 border border-blue-400 text-blue-500 font-bold px-4 py-3 mb-4' + 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> diff --git a/src/lib/form/components/Merchant.jsx b/src/lib/form/components/Merchant.jsx index 75b4e132..24b58a7c 100644 --- a/src/lib/form/components/Merchant.jsx +++ b/src/lib/form/components/Merchant.jsx @@ -103,7 +103,7 @@ const CreateMerchant = () => { <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Form Merchant</h1> <div className='w-full p-4 bg-white border border-gray_r-6 rounded'> <div - class='flex items-center bg-blue-100 border border-blue-400 text-blue-500 font-bold px-4 py-3 mb-4' + 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> diff --git a/src/lib/form/components/RequestForQuotation.jsx b/src/lib/form/components/RequestForQuotation.jsx new file mode 100644 index 00000000..31efb5d6 --- /dev/null +++ b/src/lib/form/components/RequestForQuotation.jsx @@ -0,0 +1,203 @@ +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' + +const RequestForQuotation = () => { + const { + register, + handleSubmit, + formState: { errors }, + control, + reset + } = useForm({ + resolver: yupResolver(validationSchema), + defaultValues + }) + const [cities, setCities] = useState([]) + + const quotationFileRef = useRef(null) + const recaptchaRef = useRef(null) + + useEffect(() => { + const loadCities = async () => { + let dataCities = await cityApi() + dataCities = dataCities.map((obj) => ({ value: obj.name, label: obj.name })) + setCities(dataCities) + } + loadCities() + }, []) + + const onSubmitHandler = async (values) => { + const recaptchaValue = recaptchaRef.current.getValue() + if (!recaptchaValue) { + toast.error('Recaptcha harus diisi') + return + } + + 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 + } + fileBase64 = await getFileBase64(file) + } + + const data = { + name: `Request For Quotation - ${values.company}`, + email_from: values.email, + phone: values.phone, + description: [ + `Nama Perusahaan: ${values.company}`, + `No. Telepon: ${values.phone}`, + `Kota: ${values.city}`, + `No. Handphone: ${values.mobile}`, + `Alamat Email: ${values.email}`, + `Keterangan: ${values.description}` + ].join('\n') + } + + if (fileBase64) data.file_quotation = fileBase64 + + const createLead = await createLeadApi({ data }) + if (createLead) { + toast.success('Berhasil mengirimkan formulir request for quotation') + reset() + recaptchaRef.current.reset() + } + } + 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> + + <div className='w-full grid grid-cols-1 md:grid-cols-2'> + <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. + </div> + <div> + <label className='form-label mb-2'>Nama Perusahan*</label> + <input + {...register('company')} + placeholder='PT. Indoteknik Dotcom Gemilang' + type='text' + className='form-input' + aria-invalid={errors.company?.message} + /> + <div className='text-caption-2 text-danger-500 mt-1'>{errors.company?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>No. Telepon*</label> + <input + {...register('phone')} + placeholder='021XXXXXXX' + type='text' + className='form-input' + aria-invalid={errors.phone?.message} + /> + <div className='text-caption-2 text-danger-500 mt-1'>{errors.phone?.message}</div> + </div> + + <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> + <label className='form-label mb-2'>Contact Person*</label> + <input + {...register('contactPerson')} + placeholder='John Doe' + type='text' + className='form-input' + aria-invalid={errors.contactPerson?.message} + /> + <div className='text-caption-2 text-danger-500 mt-1'>{errors.contactPerson?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>No. Handphone*</label> + <input + {...register('mobile')} + placeholder='628XXXXXXX' + type='text' + className='form-input' + aria-invalid={errors.mobile?.message} + /> + <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div> + </div> + + <div> + <label className='form-label mb-2'>Alamat Email*</label> + <input + {...register('email')} + placeholder='contoh@email.com' + type='email' + className='form-input' + aria-invalid={errors.email?.message} + /> + <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' /> + </div> + + <div> + <label className='form-label mb-2'>File Daftar Produk</label> + <input type="file" name="quotationFile" className='form-input' ref={quotationFileRef} /> + </div> + + <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'> + Simpan + </button> + </form> + </div> + </div> + ) +} + +const validationSchema = Yup.object().shape({ + 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'), + contactPerson: Yup.string().required('Harus dipilih'), +}) + +const defaultValues = { + email: '', + company: '', + phone: '', + mobile: '', + address: '', + city: '', + description: '' +} + +export default RequestForQuotation diff --git a/src/lib/form/components/SuratDukungan.jsx b/src/lib/form/components/SuratDukungan.jsx index 0eab84a4..360dd838 100644 --- a/src/lib/form/components/SuratDukungan.jsx +++ b/src/lib/form/components/SuratDukungan.jsx @@ -76,7 +76,7 @@ const CreateSuratDukungan = () => { <h1 className='text-h-sm md:text-title-sm font-semibold mb-6'>Form Surat Dukungan</h1> <div className='w-full p-4 bg-white border border-gray_r-6 rounded'> <div - class='flex items-center bg-blue-100 border border-blue-400 text-blue-500 font-bold px-4 py-3 mb-4' + 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>Lengkapi form berikut untuk melakukan konfirmasi pembayaran.</p> diff --git a/src/lib/product/components/ProductCard.jsx b/src/lib/product/components/ProductCard.jsx index 9300643e..9a5fe9a2 100644 --- a/src/lib/product/components/ProductCard.jsx +++ b/src/lib/product/components/ProductCard.jsx @@ -48,6 +48,7 @@ const ProductCard = ({ product, simpleTitle, variant = 'vertical' }) => { className={`font-normal mb-2 !text-gray_r-12 leading-6 block ${ simpleTitle ? 'line-clamp-2 h-12' : 'line-clamp-3 h-[64px]' }`} + title={product?.name} > {product?.name} </Link> diff --git a/src/lib/product/components/ProductSearch.jsx b/src/lib/product/components/ProductSearch.jsx index cc85589d..30722c82 100644 --- a/src/lib/product/components/ProductSearch.jsx +++ b/src/lib/product/components/ProductSearch.jsx @@ -248,9 +248,9 @@ const ProductSearch = ({ query, prefixUrl, defaultBrand = null }) => { <span> Barang yang anda cari tidak ada?{' '} <a - href={whatsappUrl('productSearch', { + href={query?.q ? whatsappUrl('productSearch', { name: query.q - })} + }) : whatsappUrl()} className='text-danger-500' > Hubungi Kami diff --git a/src/lib/review/api/customerReviewsApi.js b/src/lib/review/api/customerReviewsApi.js new file mode 100644 index 00000000..1058b72e --- /dev/null +++ b/src/lib/review/api/customerReviewsApi.js @@ -0,0 +1,6 @@ +import odooApi from "@/core/api/odooApi" + +export const getCustomerReviews = async () => { + const response = await odooApi('GET', '/api/v1/customer_review') + return response +}
\ No newline at end of file diff --git a/src/lib/review/components/CustomerReviews.jsx b/src/lib/review/components/CustomerReviews.jsx new file mode 100644 index 00000000..935d4a3d --- /dev/null +++ b/src/lib/review/components/CustomerReviews.jsx @@ -0,0 +1,62 @@ +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' + +const { useQuery } = require('react-query') +const { getCustomerReviews } = require('../api/customerReviewsApi') + +const CustomerReviews = () => { + const { data: customerReviews } = useQuery('customerReviews', getCustomerReviews) + + return ( + <div className='px-4 sm:px-0'> + <div className='font-medium sm:text-h-lg mb-4'>Ulasan Konsumen Kami</div> + + <DesktopView> + <Swiper slidesPerView={3.2} spaceBetween={16}> + {customerReviews && + customerReviews?.map((customerReview) => ( + <SwiperSlide className='pb-4' key={customerReview.id}> + <Card customerReview={customerReview} /> + </SwiperSlide> + ))} + </Swiper> + </DesktopView> + + <MobileView> + <Swiper slidesPerView={1.1} spaceBetween={8}> + {customerReviews && + customerReviews?.map((customerReview) => ( + <SwiperSlide className='pb-4' key={customerReview.id}> + <Card customerReview={customerReview} /> + </SwiperSlide> + ))} + </Swiper> + </MobileView> + </div> + ) +} + +const Card = ({ customerReview }) => ( + <div className='bg-gray-200 rounded-md px-5 py-6 shadow-md shadow-gray-500/20 h-full'> + <div className='flex items-center space-x-3 mb-4'> + <Image + src={customerReview.image} + alt={customerReview.customerName} + width={48} + height={48} + className='rounded-full border border-yellow-400' + /> + <div className='text-body-2 sm:text-body-1 font-semibold leading-6 text-gray-900'> + {customerReview.customerName} + </div> + </div> + <div + className='leading-7 text-gray-800' + dangerouslySetInnerHTML={{ __html: customerReview.ulasan }} + /> + </div> +) + +export default CustomerReviews diff --git a/src/lib/transaction/components/Transaction.jsx b/src/lib/transaction/components/Transaction.jsx index 30bb454a..7a1e0dd7 100644 --- a/src/lib/transaction/components/Transaction.jsx +++ b/src/lib/transaction/components/Transaction.jsx @@ -34,11 +34,11 @@ const Transaction = ({ id }) => { const file = poFile.current.files[0] const name = poNumber.current.value if (typeof file === 'undefined' || !name) { - toast.error('Nomor dan Dokumen PO harus diisi', { position: 'bottom-center' }) + toast.error('Nomor dan Dokumen PO harus diisi') return } if (file.size > 5000000) { - toast.error('Maksimal ukuran file adalah 5MB', { position: 'bottom-center' }) + toast.error('Maksimal ukuran file adalah 5MB') return } const data = { name, file: await getFileBase64(file) } diff --git a/src/lib/transaction/components/Transactions.jsx b/src/lib/transaction/components/Transactions.jsx index bfb8c66b..c1c27a51 100644 --- a/src/lib/transaction/components/Transactions.jsx +++ b/src/lib/transaction/components/Transactions.jsx @@ -224,7 +224,9 @@ const Transactions = ({ context = '' }) => { <thead> <tr> <th>No. Transaksi</th> + <th>No. PO</th> <th>Tanggal</th> + <th>Created By</th> <th className='!text-left'>Salesperson</th> <th className='!text-left'>Total</th> <th>Status</th> @@ -233,7 +235,7 @@ const Transactions = ({ context = '' }) => { <tbody> {transactions.isLoading && ( <tr> - <td colSpan={5}> + <td colSpan={7}> <div className='flex justify-center my-2'> <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> </div> @@ -244,7 +246,7 @@ const Transactions = ({ context = '' }) => { (!transactions?.data?.saleOrders || transactions?.data?.saleOrders?.length == 0) && ( <tr> - <td colSpan={5}>Tidak ada data transaksi</td> + <td colSpan={7}>Tidak ada data transaksi</td> </tr> )} {transactions.data?.saleOrders?.map((saleOrder) => ( @@ -252,7 +254,9 @@ const Transactions = ({ context = '' }) => { <td> <Link href={`${router.pathname}/${saleOrder.id}`}>{saleOrder.name}</Link> </td> - <td>-</td> + <td>{saleOrder.purchaseOrderName || '-'}</td> + <td>{saleOrder.dateOrder || '-'}</td> + <td>{saleOrder.address.customer?.name || '-'}</td> <td className='!text-left'>{saleOrder.sales}</td> <td className='!text-left'>{currencyFormat(saleOrder.amountTotal)}</td> <td> |
