diff options
Diffstat (limited to 'src/lib/address/components')
| -rw-r--r-- | src/lib/address/components/CreateAddress.jsx | 107 | ||||
| -rw-r--r-- | src/lib/address/components/EditAddress.jsx | 331 |
2 files changed, 332 insertions, 106 deletions
diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx index 963a19aa..2879fb3d 100644 --- a/src/lib/address/components/CreateAddress.jsx +++ b/src/lib/address/components/CreateAddress.jsx @@ -48,19 +48,16 @@ const CreateAddress = () => { pinedMaps, setPinedMaps } = 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); - setValue('street', detailAddress?.street); - - } - }, [detailAddress, setValue]); + + const resetPin = useMaps((state) => state.resetPin); + const [showValidationPopup, setShowValidationPopup] = useState(false); + const [popupMessage, setPopupMessage] = useState(""); + const [selectedCityName, setSelectedCityName] = useState(""); + const [normalizedDistrict, setNormalizedDistrict] = useState(""); + + useEffect(() => { + resetPin(); + }, [resetPin]); useEffect(() => { const loadState = async () => { @@ -75,6 +72,20 @@ const CreateAddress = () => { setAddressMaps(''); }, []); + 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); + setValue('street', detailAddress?.street); + + } + }, [detailAddress, setValue]); + const watchState = watch('state'); useEffect(() => { setValue('city', ''); @@ -91,19 +102,6 @@ const CreateAddress = () => { } }, [watchState, setValue]); - useEffect(() => { - if (detailAddress && Object.keys(detailAddress).length > 0) { - const selectedCities = cities.find( - (city) => - city.label.toLowerCase() === detailAddress?.district.toLowerCase() - ) || 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) { @@ -123,6 +121,29 @@ const CreateAddress = () => { } }, [auth]); + const normalizeName = (name = "") => { + return name + .toLowerCase() + .replace(/\bkabupaten\b/gi, "") + .replace(/\bkota\b/gi, "") + .trim(); + }; + + useEffect(() => { + if (detailAddress?.district) { + const normalizedDistrict = normalizeName(detailAddress.district); + + const selectedCity = cities.find((city) => { + const normalizedCity = normalizeName(city.label); + return normalizedCity === normalizedDistrict; + }); + + if (selectedCity) { + setValue("city", selectedCity.value); + } + } + }, [cities, detailAddress, setValue]); + const watchCity = watch('city'); useEffect(() => { setValue('district', ''); @@ -188,7 +209,23 @@ const CreateAddress = () => { } }, [subDistricts, detailAddress, setValue]); + + + // console.log(selectedCityName, '=', normalizedDistrict); const onSubmitHandler = async (values) => { + if (detailAddress) { + const cityName = normalizeName( + cities.find((c) => c.value === watch("city"))?.label || "" + ); + const districtName = normalizeName(detailAddress?.district || ""); + console.log(cityName, '=', districtName); + + if (cityName && cityName !== districtName) { + setPopupMessage("Titik Koordinat tidak sesuai dengan Kota yang dipilih"); + setShowValidationPopup(true); + return; + } + } const data = { ...values, state_id: values.state, @@ -219,6 +256,24 @@ const CreateAddress = () => { <PinPointMap /> </div> </BottomPopup> + <BottomPopup + active={showValidationPopup} + close={() => setShowValidationPopup(false)} + > + <div className="leading-7 text-gray_r-12/80 text-center"> + {popupMessage} + </div> + + <div className="flex justify-center mt-6"> + <button + className="btn-solid-red w-full md:w-auto" + type="button" + onClick={() => setShowValidationPopup(false)} + > + OK + </button> + </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 /> diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx index deaa8a3e..75f1a89a 100644 --- a/src/lib/address/components/EditAddress.jsx +++ b/src/lib/address/components/EditAddress.jsx @@ -1,6 +1,6 @@ import { yupResolver } from '@hookform/resolvers/yup'; import { useRouter } from 'next/router'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useMemo } from 'react'; import * as Yup from 'yup'; import cityApi from '../api/cityApi'; import { Controller, useForm } from 'react-hook-form'; @@ -44,22 +44,92 @@ const EditAddress = ({ id, defaultValues }) => { const [districts, setDistricts] = useState([]); const [subDistricts, setSubDistricts] = useState([]); const [tempAddress, setTempAddress] = useState(getValues('addressMap')); - const { addressMaps, + const resetPin = useMaps((state) => state.resetPin); + const setSelectedPosition = useMaps((state) => state.setSelectedPosition); + const setAddressMaps = useMaps((state) => state.setAddressMaps); + const setDetailAddress = useMaps((state) => state.setDetailAddress); + const [showValidationPopup, setShowValidationPopup] = useState(false); + const [popupMessage, setPopupMessage] = useState(""); + const [selectedCityName, setSelectedCityName] = useState(""); + const [normalizedDistrict, setNormalizedDistrict] = useState(""); + + const { + addressMaps, selectedPosition, detailAddress, pinedMaps, - setPinedMaps } = useMaps(); + setPinedMaps, + getDefaultCenter, // penting untuk deteksi default center + } = useMaps(); + + const normalizeName = (name = "") => { + return name + .toLowerCase() + .replace(/\bkabupaten\b/gi, "") + .replace(/\bkota\b/gi, "") + .trim(); + }; - + // Helper: cek apakah benar2 sudah PIN (bukan default center & ada addressMaps) + const isPinned = useMemo(() => { + if (!selectedPosition) return false; + + // pastikan selalu cast ke number + const lat = Number(selectedPosition.lat); + const lng = Number(selectedPosition.lng); + + // kalau hasil cast bukan angka valid + if (isNaN(lat) || isNaN(lng)) return false; + + const dc = + typeof getDefaultCenter === "function" + ? getDefaultCenter() + : { lat: -6.2, lng: 106.816666 }; + + const nearDefault = + Math.abs(lat - dc.lat) < 1e-4 && + Math.abs(lng - dc.lng) < 1e-4; + + return Boolean(addressMaps) && !nearDefault; + }, [selectedPosition, addressMaps, getDefaultCenter]); + + // Hanya isi addressMap & lat/lng di form kalau SUDAH PIN useEffect(() => { - if (addressMaps) { - setTempAddress(addressMaps); - setValue('addressMap', addressMaps); - setValue('longtitude', selectedPosition.lng); - setValue('latitude', selectedPosition.lat); + // cek kalau form punya koordinat lama + const lat = getValues("latitude"); + const lng = getValues("longtitude"); + const oldAddress = getValues("addressMap"); + + if (lat && lng) { + setTempAddress(oldAddress); + setValue("addressMap", oldAddress); + + // kalau store punya setter untuk koordinat/alamat: + if (typeof setSelectedPosition === "function") { + setSelectedPosition({ lat: Number(lat), lng: Number(lng) }); + } + if (typeof setAddressMaps === "function") { + setAddressMaps(oldAddress); + } } - }, [addressMaps, selectedPosition, setValue]); - + }, [setSelectedPosition, setAddressMaps, getValues, setValue]); + + useEffect(() => { + const addr = getValues("addressMap"); + + if (!addr || addr.trim() === "") { + resetPin(); + } else { + setAddressMaps(addr); + + const lat = getValues("latitude"); + const lng = getValues("longtitude"); + if (lat && lng) { + setSelectedPosition({ lat: Number(lat), lng: Number(lng) }); + } + } + }, [getValues, resetPin, setAddressMaps, setSelectedPosition]); + useEffect(() => { if (Object.keys(detailAddress).length > 0) { setValue('zip', detailAddress.postalCode); @@ -72,7 +142,7 @@ const EditAddress = ({ id, defaultValues }) => { setValue('street', detailAddress?.street); } }, [detailAddress, setValue]); - + useEffect(() => { const loadProfile = async () => { const dataProfile = await addressApi({ id: auth.partnerId }); @@ -83,13 +153,40 @@ const EditAddress = ({ id, defaultValues }) => { setValue('alamat_wajib_pajak', dataProfile.alamatWajibPajak); setValue('alamat_bisnis', dataProfile.alamatBisnis); setValue('business_name', dataProfile.name); - setValue('city', dataProfile.city?.id); - setValue('district', dataProfile.district?.id); - setValue('subDistrict', dataProfile.subDistrict?.id); }; if (auth) loadProfile(); - }, [auth?.parentId]); - + }, [auth?.parentId, setValue]); + + // Isi ZIP/Prov dari detailAddress (JANGAN isi street) +useEffect(() => { + const zip = getValues("zip"); + const province = getValues("state"); + const street = getValues("street"); + + // set zip dari DB kalau kosong + if (!zip && defaultValues?.zip) { + setValue("zip", defaultValues.zip); + } + + // set state dari DB kalau kosong + if (!province && defaultValues?.state) { + const selectedState = states.find( + (state) => + defaultValues.state.includes(state.label) || + state.label.includes(defaultValues.state) + ); + if (selectedState) { + setValue("state", selectedState.value); + } + } + + // set street dari DB kalau kosong + if (!street && defaultValues?.street) { + setValue("street", defaultValues.street); + } + }, [states, setValue, getValues, defaultValues]); + + useEffect(() => { const loadStates = async () => { let dataStates = await stateApi({ tempo: false }); @@ -101,7 +198,7 @@ const EditAddress = ({ id, defaultValues }) => { }; loadStates(); }, []); - + const watchState = watch('state'); useEffect(() => { setValue('city', ''); @@ -122,35 +219,46 @@ const EditAddress = ({ id, defaultValues }) => { loadCities(); } }, [watchState, setValue, getValues]); - + useEffect(() => { - if (Object.keys(detailAddress).length > 0) { - const selectedCities = cities.find( - (city) => - city.label.toLowerCase() === detailAddress?.district.toLowerCase() - ) || cities.find( + if (!isPinned) return; + + if (getValues("city")) return; + + if (Object.keys(detailAddress || {}).length > 0) { + const selectedCities = + cities.find( + (city) => + city.label.toLowerCase() === detailAddress?.district?.toLowerCase() + ) || + cities.find( (city) => - detailAddress?.district.toLowerCase().includes(city.label.toLowerCase()) || - city.label.toLowerCase().includes(detailAddress?.district.toLowerCase()) + detailAddress?.district + ?.toLowerCase() + .includes(city.label.toLowerCase()) || + city.label + .toLowerCase() + .includes(detailAddress?.district?.toLowerCase()) ); - setValue('city', selectedCities?.value); - } - }, [cities, detailAddress, setValue]); - - const watchCity = watch('city'); - useEffect(() => { - if (watchCity) { - // setValue('district', ''); - const loadDistricts = async () => { - let dataDistricts = await districtApi({ cityId: watchCity }); - dataDistricts = dataDistricts.map((district) => ({ - value: district.id, - label: district.name, + + if (selectedCities) { + setValue("city", selectedCities.value); + } + } + }, [cities, detailAddress, isPinned, getValues, setValue]); + + const watchCity = watch('city'); + useEffect(() => { + if (watchCity) { + const loadDistricts = async () => { + let dataDistricts = await districtApi({ cityId: watchCity }); + dataDistricts = dataDistricts.map((district) => ({ + value: district.id, + label: district.name, })); setDistricts(dataDistricts); let oldDistrict = getValues('oldDistrict'); if (oldDistrict) { - // setValue('district', oldDistrict); setValue('oldDistrict', ''); } }; @@ -159,6 +267,9 @@ const EditAddress = ({ id, defaultValues }) => { }, [watchCity, setValue, getValues]); useEffect(() => { + if (!isPinned) return; // skip kalau belum pin + if (getValues("district")) return; + if (Object.keys(detailAddress).length > 0) { const selectedDistrict = districts.find( (district) => @@ -173,12 +284,10 @@ const EditAddress = ({ id, defaultValues }) => { } }, [districts, detailAddress, setValue]); - - + const watchDistrict = watch('district'); useEffect(() => { if (watchDistrict) { - // setValue('subDistrict', ''); const loadSubDistricts = async () => { let dataSubDistricts = await subDistrictApi({ districtId: watchDistrict, @@ -199,8 +308,10 @@ const EditAddress = ({ id, defaultValues }) => { } }, [watchDistrict, setValue, getValues]); - useEffect(() => { + if (!isPinned) return; // skip kalau belum pin + if (getValues("subDistrict")) return; + if (Object.keys(detailAddress).length > 0) { const selectedSubDistrict = subDistricts.find( (district) => @@ -221,8 +332,39 @@ const EditAddress = ({ id, defaultValues }) => { setValue('id', id); } }, [id, setValue]); - + const onSubmitHandler = async (values) => { + if (addressMaps) { + if (!detailAddress){ + if (defaultValues?.oldCity !== values.city) { + setPopupMessage("Titik Koordinat tidak sesuai dengan Kota yang dipilih"); + setShowValidationPopup(true); + // console.log(detailAddress) + return; + } + } + if (detailAddress) { + const cityName = normalizeName( + cities.find((c) => c.value === watch("city"))?.label || "" + ); + const districtName = normalizeName(detailAddress?.district || ""); + // console.log(cityName, '=', districtName); + + if (cityName && cityName !== districtName) { + setPopupMessage("Titik Koordinat tidak sesuai dengan Kota yang dipilih"); + setShowValidationPopup(true); + return; + } + } + } + // if(!addressMaps && detailAddress){ + // if (selectedCityName && selectedCityName !== detailAddress?.district?.toLowerCase()) { + // setPopupMessage("Titik Koordinat tidak sesuai dengan Kota yang dipilih 3"); + // setShowValidationPopup(true); + // return; + // } + // } + const data = { ...values, phone: values.mobile, @@ -230,20 +372,32 @@ const EditAddress = ({ id, defaultValues }) => { city_id: parseInt(values.city, 10), district_id: parseInt(values.district, 10), sub_district_id: parseInt(values.subDistrict, 10), - longtitude: selectedPosition?.lng, - latitude: selectedPosition?.lat, - address_map: addressMaps, }; + + + if (isPinned) { + data.longtitude = selectedPosition?.lng; + data.latitude = selectedPosition?.lat; + data.address_map = addressMaps || values.addressMap; + data.use_pin = true; + } else { + data.use_pin = false; + // pastikan tidak ada nilai default center yang ikut terkirim + delete data.longtitude; + delete data.latitude; + delete data.address_map; + } + if (!auth.company) { data.alamat_lengkap_text = values.street; } + try { const address = await editAddressApi({ id, data }); console.log('Response address:', address); let isUpdated = null; - // Jika company dan partnerId sama dengan id, maka update data alamat wajib pajak const isCompanyEditingSelf = auth.company && auth.partnerId == id; if (isCompanyEditingSelf) { @@ -260,15 +414,13 @@ const EditAddress = ({ id, defaultValues }) => { mobile: values.mobile, }; - const isUpdated = await editPartnerApi({ + const isUpdatedRes = await editPartnerApi({ id: auth.partnerId, data: dataAlamat, }); - - console.log('Response isUpdated:', isUpdated); + console.log('Response isUpdated:', isUpdatedRes); } - // Validasi kondisi sukses const isSuccess = !!address?.id; if (isSuccess) { @@ -286,21 +438,10 @@ const EditAddress = ({ id, defaultValues }) => { toast.error(error?.message || 'Terjadi kesalahan tidak terduga.'); } - const dataProfile = await addressApi({ id: auth.partnerId }); - console.log('ini adalah', dataProfile); - - - // 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(); - // } + // console.log('ini adalah', dataProfile); }; - + // console.log('ini adalah', detailAddress); return ( <> <BottomPopup @@ -310,12 +451,29 @@ const EditAddress = ({ id, defaultValues }) => { close={() => setPinedMaps(false)} > <div className='flex mt-4'> - <PinPointMap - initialLatitude={selectedPosition?.lat} - initialLongitude={selectedPosition?.lng} - initialAddress={tempAddress} - /> + <PinPointMap + initialLatitude={getValues('latitude')} + initialLongitude={getValues('longtitude')} + initialAddress={getValues('addressMap')} + /> + </div> + </BottomPopup> + <BottomPopup + active={showValidationPopup} + close={() => setShowValidationPopup(false)} + > + <div className="leading-7 text-gray_r-12/80 text-center"> + {popupMessage} + </div> + <div className="flex justify-center mt-6"> + <button + className="btn-solid-red w-full md:w-auto" + type="button" + onClick={() => setShowValidationPopup(false)} + > + OK + </button> </div> </BottomPopup> <div className='max-w-none md:container mx-auto flex p-0 md:py-10'> @@ -332,17 +490,30 @@ const EditAddress = ({ id, defaultValues }) => { <form onSubmit={handleSubmit(onSubmitHandler)}> <div className='mb-4 items-start'> <label className='form-label mb-2'>Koordinat Alamat</label> - {tempAddress ? ( + {addressMaps ? ( <div className='flex gap-x-2 items-center'> - <button type='button' className="flex items-center justify-center me-3 p-2 badge-solid-red text-white rounded-full hover:bg-red-500 transition"> - <MapPinIcon class='h-6 w-6' onClick={() => setPinedMaps(true)} />{' '} - </button> - <span> {tempAddress} </span> + <button + type='button' + className='flex items-center justify-center me-3 p-2 badge-solid-red text-white rounded-full hover:bg-red-500 transition' + > + <MapPinIcon + className='h-6 w-6' + onClick={() => setPinedMaps(true)} + /> + </button> + <span> {addressMaps} </span> </div> ) : ( - <Button variant='plain' style={{ padding: 0 }} onClick={() => setPinedMaps(true)}> - <button type='button' className="flex items-center justify-center me-3 p-2 badge-solid-red text-white rounded-full hover:bg-red-500 transition"> - <MapPinIcon className="h-6 w-6" /> + <Button + variant='plain' + style={{ padding: 0 }} + onClick={() => setPinedMaps(true)} + > + <button + type='button' + className='flex items-center justify-center me-3 p-2 badge-solid-red text-white rounded-full hover:bg-red-500 transition' + > + <MapPinIcon className='h-6 w-6' /> </button> Pin Koordinat Alamat </Button> @@ -530,4 +701,4 @@ const types = [ { value: 'other', label: 'Other Address' }, ]; -export default EditAddress; +export default EditAddress;
\ No newline at end of file |
