diff options
| -rw-r--r-- | src-migrate/modules/register/components/Form.tsx | 56 | ||||
| -rw-r--r-- | src-migrate/modules/register/components/FormBisnis.tsx | 120 | ||||
| -rw-r--r-- | src-migrate/modules/register/components/RegistrasiBisnis.tsx | 8 | ||||
| -rw-r--r-- | src-migrate/modules/register/components/RegistrasiIndividu.tsx | 7 | ||||
| -rw-r--r-- | src-migrate/modules/register/index.tsx | 13 | ||||
| -rw-r--r-- | src/lib/address/components/Addresses.jsx | 140 | ||||
| -rw-r--r-- | src/lib/address/components/EditAddress.jsx | 414 | ||||
| -rw-r--r-- | src/lib/auth/components/CompanyProfile.jsx | 19 | ||||
| -rw-r--r-- | src/lib/checkout/components/Checkout.jsx | 3 | ||||
| -rw-r--r-- | src/pages/login.jsx | 12 | ||||
| -rw-r--r-- | src/pages/my/address/[id]/edit.jsx | 28 | ||||
| -rw-r--r-- | tsconfig.json | 18 |
12 files changed, 579 insertions, 259 deletions
diff --git a/src-migrate/modules/register/components/Form.tsx b/src-migrate/modules/register/components/Form.tsx index 118d9d69..cd0b4343 100644 --- a/src-migrate/modules/register/components/Form.tsx +++ b/src-migrate/modules/register/components/Form.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useMemo } from 'react'; +import { ChangeEvent, useMemo, useEffect, useRef, useState } from 'react'; import { useMutation } from 'react-query'; import { useRegisterStore } from '../stores/useRegisterStore'; import { RegisterProps } from '~/types/auth'; @@ -14,22 +14,28 @@ interface FormProps { required: boolean; isBisnisRegist: boolean; chekValid: boolean; + buttonSubmitClick: boolean; } const Form: React.FC<FormProps> = ({ type, required, isBisnisRegist = false, - chekValid = false, + chekValid, + buttonSubmitClick, }) => { const { form, isCheckedTNC, isValidCaptcha, errors, updateForm, validate } = useRegisterStore(); - const isFormValid = useMemo(() => Object.keys(errors).length === 0, [errors]); const router = useRouter(); const toast = useToast(); + const emailRef = useRef<HTMLInputElement>(null); + const nameRef = useRef<HTMLInputElement>(null); + const passwordRef = useRef<HTMLInputElement>(null); + const phoneRef = useRef<HTMLInputElement>(null); + const handleInputChange = (event: ChangeEvent<HTMLInputElement>) => { const { name, value } = event.target; updateForm(name, value); @@ -40,6 +46,38 @@ const Form: React.FC<FormProps> = ({ mutationFn: (data: RegisterProps) => registerUser(data), }); + useEffect(() => { + const loadIndustries = async () => { + const response = await mutation.mutateAsync(form); + if (!response?.register) { + const options: ScrollIntoViewOptions = { + behavior: 'smooth', + block: 'center', + }; + + if (errors.email_partner && emailRef.current) { + emailRef.current.scrollIntoView(options); + return; + } + if (errors.name && nameRef.current) { + nameRef.current.scrollIntoView(options); + return; + } + + if (errors.password && passwordRef.current) { + passwordRef.current.scrollIntoView(options); + return; + } + + if (errors.phone && phoneRef.current) { + phoneRef.current.scrollIntoView(options); + return; + } + } + }; + loadIndustries(); + }, [buttonSubmitClick, chekValid]); + const handleSubmit = async (e: ChangeEvent<HTMLFormElement>) => { e.preventDefault(); @@ -98,9 +136,10 @@ const Form: React.FC<FormProps> = ({ type='text' id='name' name='name' - className='form-input mt-3' + className='form-input mt-3 transition-all duration-700' placeholder='Masukan nama lengkap anda' value={form.name} + ref={nameRef} onChange={handleInputChange} aria-invalid={chekValid && !!errors.name} /> @@ -119,9 +158,10 @@ const Form: React.FC<FormProps> = ({ type='text' id='email' name='email' - className='form-input mt-3' + className='form-input mt-3 transition-all duration-500' placeholder='Masukan alamat email anda' value={form.email} + ref={emailRef} onChange={handleInputChange} autoComplete='username' aria-invalid={chekValid && !!errors.email} @@ -140,9 +180,10 @@ const Form: React.FC<FormProps> = ({ type='password' name='password' id='password' - className='form-input mt-3' + className='form-input mt-3 transition-all duration-500' placeholder='••••••••••••' value={form.password} + ref={passwordRef} onChange={handleInputChange} autoComplete='current-password' aria-invalid={chekValid && !!errors.password} @@ -162,9 +203,10 @@ const Form: React.FC<FormProps> = ({ type='tel' id='phone' name='phone' - className='form-input mt-3' + className='form-input mt-3 transition-all duration-500' placeholder='08xxxxxxxx' value={form.phone} + ref={phoneRef} onChange={handleInputChange} aria-invalid={chekValid && !!errors.phone} /> diff --git a/src-migrate/modules/register/components/FormBisnis.tsx b/src-migrate/modules/register/components/FormBisnis.tsx index 85bb491d..b81ca601 100644 --- a/src-migrate/modules/register/components/FormBisnis.tsx +++ b/src-migrate/modules/register/components/FormBisnis.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useEffect, useMemo, useState } from 'react'; +import { ChangeEvent, useEffect, useMemo, useRef, useState } from 'react'; import { useMutation } from 'react-query'; import { useRegisterStore } from '../stores/useRegisterStore'; import { RegisterProps } from '~/types/auth'; @@ -26,6 +26,7 @@ interface FormProps { required: boolean; isPKP: boolean; chekValid: boolean; + buttonSubmitClick: boolean; } interface industry_id { @@ -39,7 +40,13 @@ interface companyType { label: string; } -const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { +const form: React.FC<FormProps> = ({ + type, + required, + isPKP, + chekValid, + buttonSubmitClick, +}) => { const { form, errors, updateForm, validate } = useRegisterStore(); const { control, watch, setValue } = useForm(); const [selectedCategory, setSelectedCategory] = useState<string>(''); @@ -56,6 +63,18 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { const router = useRouter(); const toast = useToast(); + const emailRef = useRef<HTMLInputElement>(null); + const businessNameRef = useRef<HTMLInputElement>(null); + const companyTypeRef = useRef<HTMLInputElement>(null); + const industryRef = useRef<HTMLDivElement>(null); + const addressRef = useRef<HTMLInputElement>(null); + const namaWajibPajakRef = useRef<HTMLInputElement>(null); + const alamatWajibPajakRef = useRef<HTMLInputElement>(null); + const npwpRef = useRef<HTMLInputElement>(null); + const sppkpRef = useRef<HTMLInputElement>(null); + const docsSppkpRef = useRef<HTMLInputElement>(null); + const docsNpwpRef = useRef<HTMLInputElement>(null); + useEffect(() => { const loadCompanyTypes = async () => { const dataCompanyTypes = await odooApi( @@ -227,6 +246,60 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { mutationFn: (data: RegisterProps) => registerUser(data), }); + useEffect(() => { + const loadIndustries = async () => { + const response = await mutation.mutateAsync(form); + if (!response?.register) { + const options: ScrollIntoViewOptions = { + behavior: 'smooth', + block: 'center', + }; + if (errors.email_partner && emailRef.current) { + emailRef.current.scrollIntoView(options); + return; + } + if (errors.company_type_id && companyTypeRef.current) { + companyTypeRef.current.scrollIntoView(options); + return; + } + + if (errors.business_name && businessNameRef.current) { + businessNameRef.current.scrollIntoView(options); + return; + } + + if (errors.industry_id && industryRef.current) { + industryRef.current.scrollIntoView(options); + return; + } + + if (errors.alamat_bisnis && addressRef.current) { + addressRef.current.scrollIntoView(options); + return; + } + + if (errors.npwp && npwpRef.current) { + npwpRef.current.scrollIntoView(options); + return; + } + + if (errors.sppkp && sppkpRef.current) { + sppkpRef.current.scrollIntoView(options); + return; + } + if (errors.sppkp_document && docsSppkpRef.current) { + docsSppkpRef.current.scrollIntoView(options); + return; + } + if (errors.npwp_document && docsNpwpRef.current) { + docsNpwpRef.current.scrollIntoView(options); + return; + } + } + }; + loadIndustries(); + }, [buttonSubmitClick, chekValid]); + const handleSubmit = async (e: ChangeEvent<HTMLFormElement>) => { e.preventDefault(); @@ -314,7 +387,7 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { name='email_partner' placeholder='example@email.com' value={!required ? form.email_partner : ''} - className={`form-input max-h-11 mt-3 ${ + className={`form-input max-h-11 mt-3 transition-all duration-500 ${ required ? 'cursor-no-drop' : '' }`} disabled={required} @@ -322,6 +395,7 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { readOnly={required} onChange={handleInputChange} autoComplete='username' + ref={emailRef} aria-invalid={ chekValid && !required && isPKP && !!errors.email_partner } @@ -332,12 +406,15 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { )} </div> - <div className=''> + <div> <label className='font-bold' htmlFor='company'> Nama Bisnis </label> - <div className='flex justify-between items-start gap-2 max-h-11 min-h-11 text-sm mt-3'> - <div className='w-4/5 pr-1 max-h-11'> + <div className='flex justify-between items-start gap-2 max-h-12 min-h-12 text-sm mt-3'> + <div + className='w-4/5 pr-1 max-h-11 transition-all duration-500' + ref={companyTypeRef} + > <Controller name='companyType' control={control} @@ -361,10 +438,11 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { type='text' name='business_name' id='business_name' - className='form-input max-h-11' + className='form-input max-h-11 transition-all duration-500' placeholder='Nama Perusahaan' autoCapitalize='true' value={form.business_name} + ref={businessNameRef} aria-invalid={chekValid && !!errors.business_name} onChange={handleInputChange} /> @@ -380,7 +458,10 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { <label className='font-bold' htmlFor='business_name'> Klasifikasi Jenis Usaha </label> - <div className='max-h-10 mt-3'> + <div + className='max-h-10 transition-all duration-500' + ref={industryRef} + > <Controller name='industry_id' control={control} @@ -415,12 +496,13 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { name='alamat_bisnis' placeholder='Masukan alamat bisnis anda' value={!required ? form.alamat_bisnis : ''} - className={`form-input mt-3 max-h-11 ${ + className={`form-input mt-3 max-h-11 transition-all duration-500 ${ required ? 'cursor-no-drop' : '' }`} disabled={required} contentEditable={required} readOnly={required} + ref={addressRef} onChange={handleInputChange} aria-invalid={chekValid && !required && !!errors.alamat_bisnis} /> @@ -444,13 +526,14 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { name='nama_wajib_pajak' placeholder='Masukan nama lengkap anda' value={!required ? form.nama_wajib_pajak : ''} - className={`form-input mt-3 max-h-11 ${ + className={`form-input mt-3 max-h-11 transition-all duration-500${ required ? 'cursor-no-drop' : '' }`} disabled={required} contentEditable={required} readOnly={required} onChange={handleInputChange} + ref={namaWajibPajakRef} aria-invalid={ chekValid && isPKP && !required && !!errors.nama_wajib_pajak } @@ -472,7 +555,7 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { <span className='font-normal text-gray_r-11'>(opsional)</span> )} </p> - <div className='flex items-center ml-2 mt-1'> + <div className='flex items-center ml-2 mt-1 '> <Checkbox borderColor='gray.600' colorScheme='red' @@ -498,13 +581,14 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { : form.alamat_wajib_pajak : '' } - className={`form-input max-h-11 mt-3 ${ + className={`form-input max-h-11 mt-3 transition-all duration-500 ${ required ? 'cursor-no-drop' : '' }`} disabled={isChekBox || required} contentEditable={required} readOnly={required} onChange={handleInputChange} + ref={alamatWajibPajakRef} aria-invalid={ chekValid && isPKP && !required && !!errors.alamat_wajib_pajak } @@ -527,12 +611,13 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { type='tel' id='npwp' name='npwp' - className={`form-input max-h-11 mt-3 ${ + className={`form-input max-h-11 mt-3 transition-all duration-500 ${ required ? 'cursor-no-drop' : '' }`} disabled={required} contentEditable={required} readOnly={required} + ref={npwpRef} placeholder='000.000.000.0-000.000' value={!required ? formattedNpwp : ''} maxLength={21} // Set maximum length to 16 characters @@ -586,12 +671,13 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { type='tel' id='sppkp' name='sppkp' - className={`form-input max-h-11 mt-3 ${ + className={`form-input max-h-11 mt-3 transition-all duration-500 ${ required ? 'cursor-no-drop' : '' }`} disabled={required} contentEditable={required} readOnly={required} + ref={sppkpRef} placeholder='X-XXXPKP/WJPXXX/XX.XXXX/XXXX' onChange={handleInputChange} value={!required ? form.sppkp : ''} @@ -615,11 +701,12 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { type='file' id='npwp_document' name='npwp_document' - className={`form-input ${ + className={`form-input transition-all duration-500 ${ type === 'bisnis' ? '' : 'border-none' } mt-3 ${required ? 'cursor-no-drop' : ''}`} disabled={required} contentEditable={required} + ref={docsNpwpRef} readOnly={required} onChange={handleFileChange} accept='.pdf,.doc,.docx,.png,.jpg,.jpeg' // Filter file types @@ -642,11 +729,12 @@ const form: React.FC<FormProps> = ({ type, required, isPKP, chekValid }) => { type='file' id='sppkp_document' name='sppkp_document' - className={`form-input ${ + className={`form-input transition-all duration-500 ${ type === 'bisnis' ? '' : 'border-none' } mt-3 ${required ? 'cursor-no-drop' : ''}`} disabled={required} contentEditable={required} + ref={docsSppkpRef} readOnly={required} onChange={handleFileChange} accept='.pdf,.doc,.docx,.png,.jpg,.jpeg' // Filter file types diff --git a/src-migrate/modules/register/components/RegistrasiBisnis.tsx b/src-migrate/modules/register/components/RegistrasiBisnis.tsx index 36476ab9..ce4d3972 100644 --- a/src-migrate/modules/register/components/RegistrasiBisnis.tsx +++ b/src-migrate/modules/register/components/RegistrasiBisnis.tsx @@ -16,8 +16,12 @@ import { UseToastOptions, useToast } from '@chakra-ui/react'; import Link from 'next/link'; interface FormProps { chekValid: boolean; + buttonSubmitClick: boolean; } -const RegistrasiBisnis: React.FC<FormProps> = ({ chekValid }) => { +const RegistrasiBisnis: React.FC<FormProps> = ({ + chekValid, + buttonSubmitClick, +}) => { const [isPKP, setIsPKP] = useState(true); const [isTerdaftar, setIsTerdaftar] = useState(false); const [isDropIndividu, setIsDropIndividu] = useState(true); @@ -113,6 +117,7 @@ const RegistrasiBisnis: React.FC<FormProps> = ({ chekValid }) => { required={true} isBisnisRegist={true} chekValid={chekValid} + buttonSubmitClick={buttonSubmitClick} /> </div> )} @@ -177,6 +182,7 @@ const RegistrasiBisnis: React.FC<FormProps> = ({ chekValid }) => { required={isTerdaftar} isPKP={isPKP} chekValid={chekValid} + buttonSubmitClick={buttonSubmitClick} /> </div> )} diff --git a/src-migrate/modules/register/components/RegistrasiIndividu.tsx b/src-migrate/modules/register/components/RegistrasiIndividu.tsx index 3997e767..84049065 100644 --- a/src-migrate/modules/register/components/RegistrasiIndividu.tsx +++ b/src-migrate/modules/register/components/RegistrasiIndividu.tsx @@ -3,8 +3,12 @@ import { useRegisterStore } from '../stores/useRegisterStore'; import { useEffect } from 'react'; interface FormProps { chekValid: boolean; + buttonSubmitClick: boolean; } -const RegistrasiIndividu: React.FC<FormProps> = ({ chekValid }) => { +const RegistrasiIndividu: React.FC<FormProps> = ({ + chekValid, + buttonSubmitClick, +}) => { const { form, errors, updateForm, validate } = useRegisterStore(); useEffect(() => { @@ -21,6 +25,7 @@ const RegistrasiIndividu: React.FC<FormProps> = ({ chekValid }) => { required={false} isBisnisRegist={false} chekValid={chekValid} + buttonSubmitClick={buttonSubmitClick} /> </> ); diff --git a/src-migrate/modules/register/index.tsx b/src-migrate/modules/register/index.tsx index d91af9e3..08d7f893 100644 --- a/src-migrate/modules/register/index.tsx +++ b/src-migrate/modules/register/index.tsx @@ -22,6 +22,7 @@ const LOGO_HEIGHT = LOGO_WIDTH / 3; const Register = () => { const [isIndividuClicked, setIsIndividuClicked] = useState(true); const [notValid, setNotValid] = useState(false); + const [buttonSubmitClick, setButtonSubmitClick] = useState(false); const [isBisnisClicked, setIsBisnisClicked] = useState(false); const { form, isCheckedTNC, isValidCaptcha, resetForm, errors, updateForm } = useRegisterStore(); @@ -49,8 +50,10 @@ const Register = () => { const handleSubmit = async () => { if (!isFormValid) { setNotValid(true); + setButtonSubmitClick(!buttonSubmitClick); return; } else { + setButtonSubmitClick(!buttonSubmitClick); setNotValid(false); } const response = await mutation.mutateAsync(form); @@ -142,12 +145,18 @@ const Register = () => { <div className='transition-opacity duration-300 ease-in-out'> {isIndividuClicked && ( <div className='opacity-100'> - <RegistrasiIndividu chekValid={notValid} /> + <RegistrasiIndividu + chekValid={notValid} + buttonSubmitClick={buttonSubmitClick} + /> </div> )} {isBisnisClicked && ( <div className='opacity-100'> - <RegistrasiBisnis chekValid={notValid} /> + <RegistrasiBisnis + chekValid={notValid} + buttonSubmitClick={buttonSubmitClick} + /> </div> )} </div> diff --git a/src/lib/address/components/Addresses.jsx b/src/lib/address/components/Addresses.jsx index a610d371..9ca617ae 100644 --- a/src/lib/address/components/Addresses.jsx +++ b/src/lib/address/components/Addresses.jsx @@ -1,34 +1,72 @@ -import Link from '@/core/components/elements/Link/Link' -import Spinner from '@/core/components/elements/Spinner/Spinner' -import useAuth from '@/core/hooks/useAuth' -import { getItemAddress, updateItemAddress } from '@/core/utils/address' -import { useRouter } from 'next/router' -import useAddresses from '../hooks/useAddresses' -import MobileView from '@/core/components/views/MobileView' -import DesktopView from '@/core/components/views/DesktopView' -import Menu from '@/lib/auth/components/Menu' +import { useState } from 'react'; +import Link from '@/core/components/elements/Link/Link'; +import Spinner from '@/core/components/elements/Spinner/Spinner'; +import useAuth from '@/core/hooks/useAuth'; +import { getItemAddress, updateItemAddress } from '@/core/utils/address'; +import { useRouter } from 'next/router'; +import useAddresses from '../hooks/useAddresses'; +import MobileView from '@/core/components/views/MobileView'; +import DesktopView from '@/core/components/views/DesktopView'; +import Menu from '@/lib/auth/components/Menu'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; const Addresses = () => { - const router = useRouter() - const { select = null } = router.query - const { addresses } = useAddresses() - const selectedAddress = getItemAddress(select || '') + const router = useRouter(); + const { select = null } = router.query; + const { addresses } = useAddresses(); + const selectedAddress = getItemAddress(select || ''); + const [changeConfirmation, setChangeConfirmation] = useState(false); + const [selectedForChange, setSelectedForChange] = useState(null); // State baru untuk simpan alamat yang akan diubah + const changeSelectedAddress = (id) => { - if (!select) return - updateItemAddress(select, id) - router.back() - } + if (!select) return; + updateItemAddress(select, id); + router.back(); + }; + + const handleConfirmSubmit = () => { + setChangeConfirmation(false); + if (selectedForChange) { + router.push(`/my/address/${selectedForChange}/edit`); + } + }; if (addresses.isLoading) { return ( <div className='flex justify-center my-6'> <Spinner className='w-6 text-gray_r-12/50 fill-gray_r-12' /> </div> - ) + ); } return ( <> + <BottomPopup + active={changeConfirmation} + close={() => setChangeConfirmation(false)} // Menutup popup + title='Ubah alamat Bisnis' + > + <div className='leading-7 text-gray_r-12/80'> + Anda akan mengubah alamat utama bisnis? + </div> + <div className='flex mt-6 gap-x-4 md:justify-end'> + <button + className='btn-solid-red flex-1 md:flex-none' + type='button' + onClick={handleConfirmSubmit} + > + Yakin + </button> + <button + className='btn-light flex-1 md:flex-none' + type='button' + onClick={() => setChangeConfirmation(false)} + > + Batal + </button> + </div> + </BottomPopup> + <MobileView> <div className='p-4'> <div className='text-right'> @@ -37,7 +75,10 @@ const Addresses = () => { <div className='grid gap-y-4 mt-4'> {addresses.data?.map((address, index) => { - const type = address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address' + const type = + address.type.charAt(0).toUpperCase() + + address.type.slice(1) + + ' Address'; return ( <AddressCard key={index} @@ -45,9 +86,11 @@ const Addresses = () => { type={type} changeSelectedAddress={changeSelectedAddress} selectedAddress={selectedAddress} + setChangeConfirmation={setChangeConfirmation} // Memanggil popup + setSelectedForChange={setSelectedForChange} // Simpan id address yang akan diubah select={select} /> - ) + ); })} </div> </div> @@ -72,7 +115,9 @@ const Addresses = () => { <div className='grid grid-cols-2 gap-4'> {addresses.data?.map((address, index) => { const type = - address.type.charAt(0).toUpperCase() + address.type.slice(1) + ' Address' + address.type.charAt(0).toUpperCase() + + address.type.slice(1) + + ' Address'; return ( <AddressCard key={index} @@ -80,20 +125,31 @@ const Addresses = () => { type={type} changeSelectedAddress={changeSelectedAddress} selectedAddress={selectedAddress} + setChangeConfirmation={setChangeConfirmation} + setSelectedForChange={setSelectedForChange} select={select} /> - ) + ); })} </div> </div> </div> </DesktopView> </> - ) -} + ); +}; -const AddressCard = ({ address, selectedAddress, changeSelectedAddress, type, select }) => { - const auth = useAuth() +const AddressCard = ({ + address, + selectedAddress, + changeSelectedAddress, + type, + select, + setChangeConfirmation, + setSelectedForChange, +}) => { + const auth = useAuth(); + const router = useRouter(); return ( <div @@ -106,23 +162,37 @@ const AddressCard = ({ address, selectedAddress, changeSelectedAddress, type, se (select && 'cursor-pointer hover:bg-gray_r-4 transition') }`} > - <div onClick={() => changeSelectedAddress(address.id)} className={select && 'cursor-pointer'}> + <div + onClick={() => changeSelectedAddress(address.id)} + className={select && 'cursor-pointer'} + > <div className='flex gap-x-2'> <div className='badge-red'>{type}</div> - {auth?.partnerId == address.id && <div className='badge-green'>Utama</div>} + {auth?.partnerId == address.id && ( + <div className='badge-green'>Utama</div> + )} </div> <p className='font-medium mt-2'>{address.name}</p> - {address.mobile && <p className='mt-2 text-gray_r-11'>{address.mobile}</p>} + {address.mobile && ( + <p className='mt-2 text-gray_r-11'>{address.mobile}</p> + )} <p className='mt-1 leading-6 text-gray_r-11'>{address.street}</p> </div> - <Link - href={`/my/address/${address.id}/edit`} + <button + onClick={() => { + if (type == 'Contact Address' && auth.parentId) { + setSelectedForChange(address.id); // Set alamat yang dipilih + setChangeConfirmation(true); // Tampilkan popup konfirmasi + } else { + router.push(`/my/address/${address.id}/edit`); + } + }} className='btn-light bg-white mt-3 w-full !text-gray_r-11' > Ubah Alamat - </Link> + </button> </div> - ) -} + ); +}; -export default Addresses +export default Addresses; diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx index 520bba51..580aaeb8 100644 --- a/src/lib/address/components/EditAddress.jsx +++ b/src/lib/address/components/EditAddress.jsx @@ -1,18 +1,22 @@ -import { yupResolver } from '@hookform/resolvers/yup' -import { useRouter } from 'next/router' -import { useEffect, useState } from 'react' -import * as Yup from 'yup' -import cityApi from '../api/cityApi' -import { Controller, useForm } from 'react-hook-form' -import districtApi from '../api/districtApi' -import subDistrictApi from '../api/subDistrictApi' -import editAddressApi from '../api/editAddressApi' -import HookFormSelect from '@/core/components/elements/Select/HookFormSelect' -import { toast } from 'react-hot-toast' -import Menu from '@/lib/auth/components/Menu' +import { yupResolver } from '@hookform/resolvers/yup'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import * as Yup from 'yup'; +import cityApi from '../api/cityApi'; +import { Controller, useForm } from 'react-hook-form'; +import districtApi from '../api/districtApi'; +import subDistrictApi from '../api/subDistrictApi'; +import addressApi from '@/lib/address/api/addressApi'; +import editAddressApi from '../api/editAddressApi'; +import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; +import { toast } from 'react-hot-toast'; +import Menu from '@/lib/auth/components/Menu'; +import useAuth from '@/core/hooks/useAuth'; +import odooApi from '@/core/api/odooApi'; const EditAddress = ({ id, defaultValues }) => { - const router = useRouter() + const auth = useAuth(); + const router = useRouter(); const { register, formState: { errors }, @@ -20,205 +24,281 @@ const EditAddress = ({ id, defaultValues }) => { watch, setValue, getValues, - control + control, } = useForm({ resolver: yupResolver(validationSchema), - defaultValues - }) + defaultValues, + }); - const [cities, setCities] = useState([]) - const [districts, setDistricts] = useState([]) - const [subDistricts, setSubDistricts] = useState([]) + const [cities, setCities] = useState([]); + const [districts, setDistricts] = useState([]); + const [subDistricts, setSubDistricts] = useState([]); + + useEffect(() => { + const loadProfile = async () => { + const dataProfile = await addressApi({ id: auth.parentId }); + setValue('industry', dataProfile.industryId); + setValue('companyType', dataProfile.companyTypeId); + setValue('taxName', dataProfile.taxName); + setValue('npwp', dataProfile.npwp); + setValue('alamat_wajib_pajak', dataProfile.alamatWajibPajak); + setValue('alamat_bisnis', dataProfile.alamatBisnis); + }; + if (auth) loadProfile(); + }, [auth, setValue]); useEffect(() => { const loadCities = async () => { - let dataCities = await cityApi() + let dataCities = await cityApi(); dataCities = dataCities.map((city) => ({ value: city.id, - label: city.name - })) - setCities(dataCities) - } - loadCities() - }, []) + label: city.name, + })); + setCities(dataCities); + }; + loadCities(); + }, []); - const watchCity = watch('city') + const watchCity = watch('city'); useEffect(() => { - setValue('district', '') + setValue('district', ''); if (watchCity) { const loadDistricts = async () => { - let dataDistricts = await districtApi({ cityId: watchCity }) + let dataDistricts = await districtApi({ cityId: watchCity }); dataDistricts = dataDistricts.map((district) => ({ value: district.id, - label: district.name - })) - setDistricts(dataDistricts) - let oldDistrict = getValues('oldDistrict') + label: district.name, + })); + setDistricts(dataDistricts); + let oldDistrict = getValues('oldDistrict'); if (oldDistrict) { - setValue('district', oldDistrict) - setValue('oldDistrict', '') + setValue('district', oldDistrict); + setValue('oldDistrict', ''); } - } - loadDistricts() + }; + loadDistricts(); } - }, [watchCity, setValue, getValues]) + }, [watchCity, setValue, getValues]); - const watchDistrict = watch('district') + const watchDistrict = watch('district'); useEffect(() => { - setValue('subDistrict', '') + setValue('subDistrict', ''); if (watchDistrict) { const loadSubDistricts = async () => { let dataSubDistricts = await subDistrictApi({ - districtId: watchDistrict - }) + districtId: watchDistrict, + }); dataSubDistricts = dataSubDistricts.map((district) => ({ value: district.id, - label: district.name - })) - setSubDistricts(dataSubDistricts) - let oldSubDistrict = getValues('oldSubDistrict') + label: district.name, + })); + setSubDistricts(dataSubDistricts); + let oldSubDistrict = getValues('oldSubDistrict'); if (oldSubDistrict) { - setValue('subDistrict', oldSubDistrict) - setValue('oldSubDistrict', '') + setValue('subDistrict', oldSubDistrict); + setValue('oldSubDistrict', ''); } - } - loadSubDistricts() + }; + loadSubDistricts(); } - }, [watchDistrict, setValue, getValues]) + }, [watchDistrict, setValue, getValues]); const onSubmitHandler = async (values) => { const data = { ...values, + phone: values.mobile, city_id: values.city, district_id: values.district, - sub_district_id: values.subDistrict + sub_district_id: values.subDistrict, + }; + const address = await editAddressApi({ id, data }); + let dataAlamat; + let isUpdated = true; + if (auth?.partnerId == id) { + dataAlamat = { + ...values, + id_user: auth.partnerId, + company_type_id: values.companyType, + industry_id: values.industry, + tax_name: values.taxName, + alamat_lengkap_text: values.alamat_wajib_pajak, + street: values.street, + }; + isUpdated = await odooApi( + 'PUT', + `/api/v1/partner/${auth.parentId}`, + dataAlamat + ); } - const address = await editAddressApi({ id, data }) + // if (isUpdated?.id) { if (address?.id) { - toast.success('Berhasil mengubah alamat') - router.back() + toast.success('Berhasil mengubah alamat'); + router.back(); + } else { + toast.error('Terjadi kesalahan internal'); + router.back(); } - } + }; return ( - <div className='max-w-none md:container mx-auto flex p-0 md:py-10'> - <div className='hidden md:block w-3/12 pr-4'> - <Menu /> - </div> - <div className='w-full md:w-9/12 p-4 bg-white border border-gray_r-6 rounded'> - <h1 className='text-title-sm font-semibold mb-6 hidden md:block'>Ubah Alamat</h1> - <form onSubmit={handleSubmit(onSubmitHandler)}> - <div className='grid grid-cols-1 md:grid-cols-2 gap-4'> - <div> - <label className='form-label mb-2'>Label Alamat</label> - <Controller - name='type' - control={control} - render={(props) => ( - <HookFormSelect {...props} isSearchable={false} options={types} /> - )} - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.type?.message}</div> - </div> + <> + <div className='max-w-none md:container mx-auto flex p-0 md:py-10'> + <div className='hidden md:block w-3/12 pr-4'> + <Menu /> + </div> + <div className='w-full md:w-9/12 p-4 bg-white border border-gray_r-6 rounded'> + <div className='flex justify-start items-center mb-6'> + <h1 className='text-title-sm font-semibold hidden md:block mr-2'> + Ubah Alamat + </h1> + {auth?.partnerId == id && <div className='badge-green'>Utama</div>} + </div> + <form onSubmit={handleSubmit(onSubmitHandler)}> + <div className='grid grid-cols-1 md:grid-cols-2 gap-4'> + <div> + <label className='form-label mb-2'>Label Alamat</label> + <Controller + name='type' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + isSearchable={false} + options={types} + /> + )} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.type?.message} + </div> + </div> - <div> - <label className='form-label mb-2'>Nama</label> - <input - {...register('name')} - placeholder='John Doe' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.name?.message}</div> - </div> + <div> + <label className='form-label mb-2'>Nama</label> + <input + {...register('name')} + placeholder='John Doe' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.name?.message} + </div> + </div> - <div> - <label className='form-label mb-2'>Email</label> - <input - {...register('email')} - placeholder='johndoe@example.com' - type='email' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.email?.message}</div> - </div> + <div> + <label className='form-label mb-2'>Email</label> + <input + {...register('email')} + placeholder='johndoe@example.com' + type='email' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.email?.message} + </div> + </div> - <div> - <label className='form-label mb-2'>Mobile</label> - <input - {...register('mobile')} - placeholder='08xxxxxxxx' - type='tel' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.mobile?.message}</div> - </div> + <div> + <label className='form-label mb-2'>Mobile</label> + <input + {...register('mobile')} + placeholder='08xxxxxxxx' + type='tel' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.mobile?.message} + </div> + </div> - <div> - <label className='form-label mb-2'>Alamat</label> - <input - {...register('street')} - placeholder='Jl. Bandengan Utara 85A' - type='text' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.street?.message}</div> - </div> + <div> + <label className='form-label mb-2'>Alamat</label> + <input + {...register('street')} + placeholder='Jl. Bandengan Utara 85A' + type='text' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.street?.message} + </div> + </div> - <div> - <label className='form-label mb-2'>Kode Pos</label> - <input - {...register('zip')} - placeholder='10100' - type='number' - className='form-input' - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.zip?.message}</div> - </div> + <div> + <label className='form-label mb-2'>Kode Pos</label> + <input + {...register('zip')} + placeholder='10100' + type='number' + className='form-input' + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.zip?.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'>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'>Kecamatan</label> - <Controller - name='district' - control={control} - render={(props) => ( - <HookFormSelect {...props} options={districts} disabled={!watchCity} /> - )} - /> - <div className='text-caption-2 text-danger-500 mt-1'>{errors.district?.message}</div> - </div> + <div> + <label className='form-label mb-2'>Kecamatan</label> + <Controller + name='district' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={districts} + disabled={!watchCity} + /> + )} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.district?.message} + </div> + </div> - <div> - <label className='form-label mb-2'>Kelurahan</label> - <Controller - name='subDistrict' - control={control} - render={(props) => ( - <HookFormSelect {...props} options={subDistricts} disabled={!watchDistrict} /> - )} - /> + <div> + <label className='form-label mb-2'>Kelurahan</label> + <Controller + name='subDistrict' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={subDistricts} + disabled={!watchDistrict} + /> + )} + /> + </div> </div> - </div> - <button type='submit' className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto'> - Simpan - </button> - </form> + <button + type='submit' + className='btn-yellow w-full md:w-fit mt-6 ml-0 md:ml-auto' + > + Simpan + </button> + </form> + </div> </div> - </div> - ) -} + </> + ); +}; const validationSchema = Yup.object().shape({ type: Yup.string().required('Harus di-pilih'), @@ -228,14 +308,14 @@ const validationSchema = Yup.object().shape({ street: Yup.string().required('Harus di-isi'), zip: Yup.string().required('Harus di-isi'), city: Yup.string().required('Harus di-pilih'), - district: Yup.string().required('Harus di-pilih') -}) + district: Yup.string().required('Harus di-pilih'), +}); const types = [ { value: 'contact', label: 'Contact Address' }, { value: 'invoice', label: 'Invoice Address' }, { value: 'delivery', label: 'Delivery Address' }, - { value: 'other', label: 'Other Address' } -] + { value: 'other', label: 'Other Address' }, +]; -export default EditAddress +export default EditAddress; diff --git a/src/lib/auth/components/CompanyProfile.jsx b/src/lib/auth/components/CompanyProfile.jsx index 20be6829..cebb15b0 100644 --- a/src/lib/auth/components/CompanyProfile.jsx +++ b/src/lib/auth/components/CompanyProfile.jsx @@ -21,9 +21,9 @@ const CompanyProfile = () => { taxName: '', npwp: '', alamat_wajib_pajak: '', + alamat_bisnis: '', }, }); - const [industries, setIndustries] = useState([]); useEffect(() => { const loadIndustries = async () => { @@ -73,6 +73,7 @@ const CompanyProfile = () => { industry_id: values.industry, tax_name: values.taxName, alamat_lengkap_text: values.alamat_wajib_pajak, + street: values.alamat_bisnis, }; const isUpdated = await odooApi( 'PUT', @@ -185,17 +186,25 @@ const CompanyProfile = () => { /> </div> <div> - <label>Nomor NPWP</label> + <label>Alamat Wajib Pajak</label> <input - {...register('npwp')} + {...register('alamat_wajib_pajak')} type='text' className='form-input mt-3' /> </div> <div> - <label>Alamat Wajib Pajak</label> + <label>Alamat Bisnis</label> <input - {...register('alamat_wajib_pajak')} + {...register('alamat_bisnis')} + type='text' + className='form-input mt-3' + /> + </div> + <div> + <label>Nomor NPWP</label> + <input + {...register('npwp')} type='text' className='form-input mt-3' /> diff --git a/src/lib/checkout/components/Checkout.jsx b/src/lib/checkout/components/Checkout.jsx index 09a791ee..52f1b3ed 100644 --- a/src/lib/checkout/components/Checkout.jsx +++ b/src/lib/checkout/components/Checkout.jsx @@ -77,6 +77,9 @@ const Checkout = () => { if (!addresses) return; const matchAddress = (key) => { + if (key === 'invoicing') { + key = 'invoice'; + } const addressToMatch = getItemAddress(key); const foundAddress = addresses.filter( (address) => address.id == addressToMatch diff --git a/src/pages/login.jsx b/src/pages/login.jsx index 9a1aa85b..07d13784 100644 --- a/src/pages/login.jsx +++ b/src/pages/login.jsx @@ -1,3 +1,5 @@ +import { useEffect } from 'react'; +import { useRouter } from 'next/router'; import Seo from '@/core/components/Seo'; import SimpleFooter from '@/core/components/elements/Footer/SimpleFooter'; import BasicLayout from '@/core/components/layouts/BasicLayout'; @@ -5,8 +7,18 @@ import DesktopView from '@/core/components/views/DesktopView'; import MobileView from '@/core/components/views/MobileView'; import LoginComponent from '@/lib/auth/components/Login'; import AccountActivation from '~/modules/account-activation'; +import useAuth from '@/core/hooks/useAuth'; export default function Login() { + const router = useRouter(); + const auth = useAuth(); + + useEffect(() => { + if (auth) { + router.push('/'); + } + }, [auth, router]); + return ( <> <Seo title='Login - Indoteknik.com' /> diff --git a/src/pages/my/address/[id]/edit.jsx b/src/pages/my/address/[id]/edit.jsx index bd680b90..732ec9fc 100644 --- a/src/pages/my/address/[id]/edit.jsx +++ b/src/pages/my/address/[id]/edit.jsx @@ -1,11 +1,11 @@ -import Seo from '@/core/components/Seo' -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 addressApi from '@/lib/address/api/addressApi' -import EditAddressComponent from '@/lib/address/components/EditAddress' -import IsAuth from '@/lib/auth/components/IsAuth' +import Seo from '@/core/components/Seo'; +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 addressApi from '@/lib/address/api/addressApi'; +import EditAddressComponent from '@/lib/address/components/EditAddress'; +import IsAuth from '@/lib/auth/components/IsAuth'; export default function EditAddress({ id, defaultValues }) { return ( @@ -24,12 +24,12 @@ export default function EditAddress({ id, defaultValues }) { </BasicLayout> </DesktopView> </IsAuth> - ) + ); } export async function getServerSideProps(context) { - const { id } = context.query - const address = await addressApi({ id }) + const { id } = context.query; + const address = await addressApi({ id }); const defaultValues = { type: address.type, name: address.name, @@ -41,7 +41,7 @@ export async function getServerSideProps(context) { oldDistrict: address.district?.id || '', district: '', oldSubDistrict: address.subDistrict?.id || '', - subDistrict: '' - } - return { props: { id, defaultValues } } + subDistrict: '', + }; + return { props: { id, defaultValues } }; } diff --git a/tsconfig.json b/tsconfig.json index 8613c022..96670169 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,11 +1,7 @@ { "compilerOptions": { "target": "ES5", - "lib": [ - "DOM", - "DOM.Iterable", - "ESNext" - ], + "lib": ["DOM", "DOM.Iterable", "ESNext"], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -33,10 +29,10 @@ "**/*.ts", "**/*.tsx", "**/*.jsx", - ".next/types/**/*.ts" -, "src/pages/shop/promo/index.tsx", "src/pages/shop/promo/[slug].jsx" ], - "exclude": [ - "node_modules", - "src" - ] + ".next/types/**/*.ts", + "src-migrate/**/*", + "src/pages/shop/promo/index.tsx", + "src/pages/shop/promo/[slug].jsx" + ], + "exclude": ["node_modules", "src"] } |
