diff options
Diffstat (limited to 'src/lib/address')
| -rw-r--r-- | src/lib/address/components/Addresses.jsx | 17 | ||||
| -rw-r--r-- | src/lib/address/components/CreateAddress.jsx | 400 | ||||
| -rw-r--r-- | src/lib/address/components/EditAddress.jsx | 116 |
3 files changed, 356 insertions, 177 deletions
diff --git a/src/lib/address/components/Addresses.jsx b/src/lib/address/components/Addresses.jsx index 9ca617ae..1007b9f8 100644 --- a/src/lib/address/components/Addresses.jsx +++ b/src/lib/address/components/Addresses.jsx @@ -9,6 +9,7 @@ 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'; +import { MapPinIcon } from 'lucide-react'; const Addresses = () => { const router = useRouter(); @@ -17,7 +18,7 @@ const Addresses = () => { 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); @@ -177,6 +178,20 @@ const AddressCard = ({ <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 className='flex items-center mt-4'> + {address.addressMap ? ( + <> + <MapPinIcon class='h-7 w-8 text-yellow-600 mr-3 font-semibold' /> + <p className='text-yellow-600 font-semibold'>Sudah PinPoint</p> + </> + ) : ( + <> + <MapPinIcon class='h-7 w-8 text-red-600 mr-2 font-semibold' /> + <p className='text-red-600 font-semibold text-sm'>Belum PinPoint</p> + </> + )} + </div> </div> <button onClick={() => { diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx index 9d70e8fc..404143f9 100644 --- a/src/lib/address/components/CreateAddress.jsx +++ b/src/lib/address/components/CreateAddress.jsx @@ -1,18 +1,24 @@ import HookFormSelect from '@/core/components/elements/Select/HookFormSelect'; import useAuth from '@/core/hooks/useAuth'; +import Menu from '@/lib/auth/components/Menu'; +import { yupResolver } from '@hookform/resolvers/yup'; import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; +import { toast } from 'react-hot-toast'; import * as Yup from 'yup'; import cityApi from '../api/cityApi'; +import createAddressApi from '../api/createAddressApi'; import districtApi from '../api/districtApi'; +import stateApi from '../api/stateApi'; import subDistrictApi from '../api/subDistrictApi'; -import { useEffect, useState } from 'react'; -import createAddressApi from '../api/createAddressApi'; -import { toast } from 'react-hot-toast'; -import { yupResolver } from '@hookform/resolvers/yup'; -import Menu from '@/lib/auth/components/Menu'; import useAddresses from '../hooks/useAddresses'; -import stateApi from '../api/stateApi'; + +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; +import { Button } from '@chakra-ui/react'; +import { MapPinIcon } from 'lucide-react'; +import PinPointMap from '../../maps/components/PinPointMap'; +import { useMaps } from '../../maps/stores/useMaps'; const CreateAddress = () => { const auth = useAuth(); @@ -34,6 +40,25 @@ const CreateAddress = () => { const [districts, setDistricts] = useState([]); const [subDistricts, setSubDistricts] = useState([]); const [filteredTypes, setFilteredTypes] = useState(types); // State to manage filtered types + const [pinedMaps, setPinedMaps] = useState(false); + const { + addressMaps, + selectedPosition, + detailAddress, + } = useMaps(); + + useEffect(() => { + if (detailAddress) { + setValue('zip', detailAddress.postalCode); + const selectedState = states.find( + (state) => + detailAddress?.province.includes(state.label) || + state.label.includes(detailAddress?.province) + ); + setValue('state', selectedState?.value); + + } + }, [detailAddress, setValue]); useEffect(() => { const loadState = async () => { @@ -48,11 +73,12 @@ const CreateAddress = () => { }, []); const watchState = watch('state'); + console.log(watchState); useEffect(() => { setValue('city', ''); if (watchState) { const loadCities = async () => { - let dataCities = await cityApi({stateId: watchState}); + let dataCities = await cityApi({ stateId: watchState }); dataCities = dataCities.map((city) => ({ value: city.id, label: city.name, @@ -64,6 +90,21 @@ const CreateAddress = () => { }, [watchState, setValue]); useEffect(() => { + if (detailAddress) { + const selectedCities = cities.find( + (city) => + detailAddress.district + .toLowerCase() + .includes(city.label.toLowerCase()) || + city.label + .toLowerCase() + .includes(detailAddress.district.toLowerCase()) + ); + setValue('city', selectedCities?.value); + } + }, [cities, detailAddress, setValue]); + + useEffect(() => { if (addresses) { let hasContactAddress = false; @@ -97,6 +138,21 @@ const CreateAddress = () => { } }, [watchCity, setValue]); + useEffect(() => { + if (detailAddress) { + const selectedDistrict = districts.find( + (district) => + detailAddress.subDistrict + .toLowerCase() + .includes(district.label.toLowerCase()) || + district.label + .toLowerCase() + .includes(detailAddress.subDistrict.toLowerCase()) + ); + setValue('district', selectedDistrict?.value); + } + }, [districts, detailAddress, setValue]); + const watchDistrict = watch('district'); useEffect(() => { setValue('subDistrict', ''); @@ -115,6 +171,22 @@ const CreateAddress = () => { } }, [watchDistrict, setValue]); + useEffect(() => { + if (detailAddress) { + const selectedSubDistrict = subDistricts.find( + (district) => + detailAddress.village + .toLowerCase() + .includes(district.label.toLowerCase()) || + district.label + .toLowerCase() + .includes(detailAddress.village.toLowerCase()) + ); + + setValue('subDistrict', selectedSubDistrict?.value); + } + }, [subDistricts, detailAddress, setValue]); + const onSubmitHandler = async (values) => { const data = { ...values, @@ -123,8 +195,10 @@ const CreateAddress = () => { district_id: values.district, sub_district_id: values.subDistrict, parent_id: auth.partnerId, + latitude: selectedPosition?.lat, + longtitude: selectedPosition?.lng, + address_map: addressMaps, }; - const address = await createAddressApi({ data }); if (address?.id) { toast.success('Berhasil menambahkan alamat'); @@ -133,167 +207,197 @@ const CreateAddress = () => { }; 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'> - <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={filteredTypes} - /> - )} - /> - <div className='text-caption-2 text-danger-500 mt-1'> - {errors.type?.message} - </div> + <> + <BottomPopup + className=' !h-[75%]' + title='Pin Maps Address' + active={pinedMaps} + close={() => setPinedMaps(false)} + > + <div className='flex mt-4'> + <PinPointMap /> + </div> + </BottomPopup> + <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'> + <form onSubmit={handleSubmit(onSubmitHandler)}> + <div className='mb-4 items-start'> + <label className='form-label mb-2'>PinPoint</label> + {addressMaps ? ( + <div className='flex gap-x-2 items-center'> + <MapPinIcon class='h-6 w-6 text-gray-500 mr-3' />{' '} + <span> {addressMaps} </span> + </div> + ) : ( + <Button variant='plain' onClick={() => setPinedMaps(true)}> + <MapPinIcon class='h-6 w-6 text-gray-500 mr-3' /> + Pin Alamat + </Button> + )} </div> + <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={filteredTypes} + /> + )} + /> + <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> + <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> - <div> - <label className='form-label mb-2'>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> + <label className='form-label mb-2'>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> - <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> + <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> - <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> + <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> - <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> + <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> - <div> - <label className='form-label mb-2'>Provinsi</label> - <Controller - name='state' - control={control} - render={(props) => ( - <HookFormSelect {...props} options={states} /> - )} - /> - <div className='text-caption-2 text-danger-500 mt-1'> - {errors.state?.message} + <div> + <label className='form-label mb-2'>Provinsi</label> + <Controller + name='state' + control={control} + render={(props) => ( + <HookFormSelect {...props} options={states} /> + )} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.state?.message} + </div> </div> - </div> - <div> - <label className='form-label mb-2'>Kota</label> - <Controller - name='city' - control={control} - render={(props) => ( - <HookFormSelect {...props} options={cities} disabled={!watchState}/> - )} - /> - <div className='text-caption-2 text-danger-500 mt-1'> - {errors.city?.message} + <div> + <label className='form-label mb-2'>Kota</label> + <Controller + name='city' + control={control} + render={(props) => ( + <HookFormSelect + {...props} + options={cities} + disabled={!watchState} + /> + )} + /> + <div className='text-caption-2 text-danger-500 mt-1'> + {errors.city?.message} + </div> </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> + <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> - <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> + </> ); }; diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx index 23cf72a9..0b3b0aa3 100644 --- a/src/lib/address/components/EditAddress.jsx +++ b/src/lib/address/components/EditAddress.jsx @@ -14,6 +14,12 @@ import Menu from '@/lib/auth/components/Menu'; import useAuth from '@/core/hooks/useAuth'; import odooApi from '@/core/api/odooApi'; import stateApi from '../api/stateApi'; +import { MapPinIcon } from 'lucide-react'; +import { Button } from '@chakra-ui/react'; +import { useMaps } from '../../maps/stores/useMaps'; + +import PinPointMap from '../../maps/components/PinPointMap'; +import BottomPopup from '@/core/components/elements/Popup/BottomPopup'; const EditAddress = ({ id, defaultValues }) => { const auth = useAuth(); @@ -34,7 +40,19 @@ const EditAddress = ({ id, defaultValues }) => { const [states, setStates] = useState([]); const [cities, setCities] = useState([]); const [districts, setDistricts] = useState([]); - const [subDistricts, setSubDistricts] = useState([]); + const [subDistricts, setSubDistricts] = useState([]); + const [pinedMaps, setPinedMaps] = useState(false); + const [tempAddress, setTempAddress] = useState(getValues('address_maps')); + const { addressMaps, selectedPosition, detailAddress } = useMaps(); + + useEffect(() => { + if (addressMaps) { + setTempAddress(addressMaps); + setValue('address_map', addressMaps); + setValue('longtitude', selectedPosition.lng); + setValue('latitude', selectedPosition.lat); + } + }, [addressMaps, selectedPosition, setValue]); useEffect(() => { const loadProfile = async () => { @@ -60,12 +78,12 @@ const EditAddress = ({ id, defaultValues }) => { setStates(dataStates); }; loadStates(); - },[]) + }, []); const watchState = watch('state'); useEffect(() => { setValue('city', ''); - if(watchState) { + if (watchState) { const loadCities = async () => { let dataCities = await cityApi({ stateId: watchState }); dataCities = dataCities.map((city) => ({ @@ -81,7 +99,6 @@ const EditAddress = ({ id, defaultValues }) => { }; loadCities(); } - }, [watchState, setValue, getValues]); const watchCity = watch('city'); @@ -136,40 +153,65 @@ const EditAddress = ({ id, defaultValues }) => { city_id: values.city, district_id: values.district, sub_district_id: values.subDistrict, + longtitude: selectedPosition?.lng, + latitude: selectedPosition?.lat, + address_map: addressMaps, }; if (!auth.company) { data.alamat_lengkap_text = values.street; } - const address = await editAddressApi({ id, data }); - let dataAlamat; - let isUpdated = true; - if (auth.company) { - if (auth?.partnerId == id) { - dataAlamat = { - id_user: auth.partnerId, - alamat_lengkap_text: values.alamat_wajib_pajak, - street: values.street, - }; - isUpdated = await odooApi( - 'PUT', - `/api/v1/partner/${auth.parentId}`, - dataAlamat - ); + try { + const address = await editAddressApi({ id, data }); + let dataAlamat; + let isUpdated = true; + if (auth.company) { + if (auth?.partnerId == id) { + dataAlamat = { + id_user: auth.partnerId, + alamat_lengkap_text: values.alamat_wajib_pajak, + street: values.street, + }; + isUpdated = await odooApi( + 'PUT', + `/api/v1/partner/${auth.parentId}`, + dataAlamat + ); + } } - } - - // if (isUpdated?.id) { - if (address?.id && auth.company ? isUpdated?.id : true) { - toast.success('Berhasil mengubah alamat'); - router.back(); - } else { + if (address?.id) { + toast.success('Berhasil mengubah alamat'); + router.back(); + } else { + toast.error('Terjadi kesalahan internal'); + router.back(); + } + } catch (error) { toast.error('Terjadi kesalahan internal'); router.back(); } + + // if (isUpdated?.id) { + // if (address?.id && auth.company ? isUpdated?.id : true) { + // toast.success('Berhasil mengubah alamat'); + // router.back(); + // } else { + // toast.error('Terjadi kesalahan internal'); + // router.back(); + // } }; return ( <> + <BottomPopup + className=' !h-[75%]' + title='Pin Maps Address' + active={pinedMaps} + close={() => setPinedMaps(false)} + > + <div className='flex mt-4'> + <PinPointMap /> + </div> + </BottomPopup> <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 /> @@ -182,6 +224,20 @@ const EditAddress = ({ id, defaultValues }) => { {auth?.partnerId == id && <div className='badge-green'>Utama</div>} </div> <form onSubmit={handleSubmit(onSubmitHandler)}> + <div className='mb-4 items-start'> + <label className='form-label mb-2'>PinPoint</label> + {tempAddress ? ( + <div className='flex gap-x-2 items-center'> + <MapPinIcon class='h-6 w-6 text-gray-500 mr-3' />{' '} + <span> {tempAddress} </span> + </div> + ) : ( + <Button variant='plain' onClick={() => setPinedMaps(true)}> + <MapPinIcon class='h-6 w-6 text-gray-500 mr-3' /> + Pin Alamat + </Button> + )} + </div> <div className='grid grid-cols-1 md:grid-cols-2 gap-4'> <div> <label className='form-label mb-2'>Label Alamat</label> @@ -287,7 +343,11 @@ const EditAddress = ({ id, defaultValues }) => { name='city' control={control} render={(props) => ( - <HookFormSelect {...props} options={cities} disabled={!watchState} /> + <HookFormSelect + {...props} + options={cities} + disabled={!watchState} + /> )} /> <div className='text-caption-2 text-danger-500 mt-1'> @@ -348,7 +408,7 @@ const validationSchema = Yup.object().shape({ mobile: Yup.string().required('Harus di-isi'), street: Yup.string().required('Harus di-isi'), zip: Yup.string().required('Harus di-isi'), - state : Yup.string().required('Harus di-pilih'), + state: Yup.string().required('Harus di-pilih'), city: Yup.string().required('Harus di-pilih'), district: Yup.string().required('Harus di-pilih'), }); |
