diff options
| author | Rafi Zadanly <zadanlyr@gmail.com> | 2023-05-10 15:11:55 +0700 |
|---|---|---|
| committer | Rafi Zadanly <zadanlyr@gmail.com> | 2023-05-10 15:11:55 +0700 |
| commit | b027c12d678698a9b8adcf4508a6160321c8172a (patch) | |
| tree | 6eb264d8404928fd20c802d1b562acce15c76afb | |
| parent | 07817bbdc1f1ae29c848e5dfdb3ac5b140f48cc4 (diff) | |
Feature rfq, social media url, widget whatsapp offset
| -rw-r--r-- | public/images/socials/Instagram.png | bin | 0 -> 24076 bytes | |||
| -rw-r--r-- | src/core/components/elements/Footer/BasicFooter.jsx | 24 | ||||
| -rw-r--r-- | src/core/components/layouts/BasicLayout.jsx | 2 | ||||
| -rw-r--r-- | src/lib/form/components/RequestForQuotation.jsx | 203 | ||||
| -rw-r--r-- | src/lib/product/components/ProductCard.jsx | 1 | ||||
| -rw-r--r-- | src/lib/transaction/components/Transaction.jsx | 4 | ||||
| -rw-r--r-- | src/pages/my/request-for-quotation.jsx | 15 |
7 files changed, 242 insertions, 7 deletions
diff --git a/public/images/socials/Instagram.png b/public/images/socials/Instagram.png Binary files differnew file mode 100644 index 00000000..6a494b5e --- /dev/null +++ b/public/images/socials/Instagram.png diff --git a/src/core/components/elements/Footer/BasicFooter.jsx b/src/core/components/elements/Footer/BasicFooter.jsx index d340ff3e..75e9926d 100644 --- a/src/core/components/elements/Footer/BasicFooter.jsx +++ b/src/core/components/elements/Footer/BasicFooter.jsx @@ -159,6 +159,9 @@ const Form = () => ( <div className={`${headerClassName} mb-3`}>Formulir</div> <ul className='flex flex-col gap-y-2'> <li> + <InternalItemLink href='/my/request-for-quotation'>Request for Quotation</InternalItemLink> + </li> + <li> <InternalItemLink href='/my/kunjungan-sales'>Kunjungan Sales</InternalItemLink> </li> <li> @@ -219,10 +222,23 @@ const SocialMedias = () => ( <div> <div className={headerClassName + 'block md:hidden'}>Temukan Kami</div> <div className='flex flex-wrap gap-2 mt-2'> - <NextImage src='/images/socials/Whatsapp.png' alt='Whatsapp Logo' width={24} height={24} /> - <NextImage src='/images/socials/Facebook.png' alt='Facebook Logo' width={24} height={24} /> - <NextImage src='/images/socials/Twitter.png' alt='Twitter Logo' width={24} height={24} /> - <NextImage src='/images/socials/Linkedin.png' alt='Linkedin Logo' width={24} height={24} /> + <a target='_blank' rel='noreferrer' href={whatsappUrl(null)}> + <NextImage src='/images/socials/Whatsapp.png' alt='Whatsapp Logo' width={24} height={24} /> + </a> + <a target='_blank' rel='noreferrer' href='https://www.facebook.com/indoteknikcom'> + <NextImage src='/images/socials/Facebook.png' alt='Facebook Logo' width={24} height={24} /> + </a> + <a target='_blank' rel='noreferrer' href='https://www.instagram.com/indoteknikcom/'> + <NextImage + src='/images/socials/Instagram.png' + alt='Instagram Logo' + width={24} + height={24} + /> + </a> + <a target='_blank' rel='noreferrer' href='https://www.linkedin.com/company/pt-indoteknik-dotcom-gemilang/'> + <NextImage src='/images/socials/Linkedin.png' alt='Linkedin Logo' width={24} height={24} /> + </a> </div> </div> ) diff --git a/src/core/components/layouts/BasicLayout.jsx b/src/core/components/layouts/BasicLayout.jsx index df42e403..04b189f9 100644 --- a/src/core/components/layouts/BasicLayout.jsx +++ b/src/core/components/layouts/BasicLayout.jsx @@ -12,7 +12,7 @@ const BasicLayout = ({ children }) => { <Navbar /> <AnimationLayout> {children} - <div className='fixed bottom-4 right-4 sm:bottom-10 sm:right-8 z-50'> + <div className='fixed bottom-4 right-4 sm:bottom-14 sm:right-10 z-50'> <a href={whatsappUrl(null)} className='py-2 pl-3 pr-4 rounded-full bg-[#4FB84A] border border-green-300 flex items-center' diff --git a/src/lib/form/components/RequestForQuotation.jsx b/src/lib/form/components/RequestForQuotation.jsx new file mode 100644 index 00000000..cd8fbfd6 --- /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'>Kunjungan Sales</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/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/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/pages/my/request-for-quotation.jsx b/src/pages/my/request-for-quotation.jsx new file mode 100644 index 00000000..40fda009 --- /dev/null +++ b/src/pages/my/request-for-quotation.jsx @@ -0,0 +1,15 @@ +import Seo from '@/core/components/Seo' +import BasicLayout from '@/core/components/layouts/BasicLayout' +import FormRequestForQuotation from '@/lib/form/components/RequestForQuotation' + +export default function RequestForQuotation() { + return ( + <> + <Seo title='Request for Quotation - Indoteknik.com' /> + + <BasicLayout> + <FormRequestForQuotation /> + </BasicLayout> + </> + ) +} |
