From 776d26572e69fa9d0b57b586262e4bd86b21dd92 Mon Sep 17 00:00:00 2001 From: it-fixcomart Date: Mon, 8 Sep 2025 15:04:49 +0700 Subject: validation Address --- src/lib/address/components/CreateAddress.jsx | 82 +++++++--- src/lib/address/components/EditAddress.jsx | 223 +++++++++++++++++++++------ src/lib/checkout/components/Checkout.jsx | 45 +++--- src/lib/maps/components/PinPointMap.jsx | 21 ++- src/lib/maps/stores/useMaps.js | 16 +- 5 files changed, 296 insertions(+), 91 deletions(-) diff --git a/src/lib/address/components/CreateAddress.jsx b/src/lib/address/components/CreateAddress.jsx index 963a19aa..fcfad056 100644 --- a/src/lib/address/components/CreateAddress.jsx +++ b/src/lib/address/components/CreateAddress.jsx @@ -48,19 +48,39 @@ const CreateAddress = () => { pinedMaps, setPinedMaps } = useMaps(); + + const resetPin = useMaps((state) => state.resetPin); + const [showValidationPopup, setShowValidationPopup] = useState(false); + const [popupMessage, setPopupMessage] = useState(""); + const [selectedCityName, setSelectedCityName] = useState(""); + + useEffect(() => { + resetPin(); + }, [resetPin]); + + useEffect(() => { - if (detailAddress) { - setValue('zip', detailAddress.postalCode); + if (defaultValues?.zip) { + setValue("zip", defaultValues.zip); + } + + // set state/province + if (defaultValues?.province) { const selectedState = states.find( (state) => - detailAddress?.province.includes(state.label) || - state.label.includes(detailAddress?.province) + defaultValues.province.includes(state.label) || + state.label.includes(defaultValues.province) ); - setValue('state', selectedState?.value); - setValue('street', detailAddress?.street); - + if (selectedState) { + setValue("state", selectedState.value); + } } - }, [detailAddress, setValue]); + + // set street + if (defaultValues?.street) { + setValue("street", defaultValues.street); + } + }, [states, setValue, defaultValues]); useEffect(() => { const loadState = async () => { @@ -91,19 +111,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) { @@ -188,7 +195,22 @@ const CreateAddress = () => { } }, [subDistricts, detailAddress, setValue]); + useEffect(() => { + const currentCity = cities.find((c) => c.value === watch("city"))?.label || ""; + setSelectedCityName(currentCity); + }, [watch("city"), cities]); + const onSubmitHandler = async (values) => { + if (detailAddress?.district) { + if ( + selectedCityName && + selectedCityName.toLowerCase() !== detailAddress?.district?.toLowerCase() + ) { + setPopupMessage("Titik Koordinat tidak sesuai dengan Kota yang dipilih"); + setShowValidationPopup(true); + return; + } + } const data = { ...values, state_id: values.state, @@ -219,6 +241,24 @@ const CreateAddress = () => { + setShowValidationPopup(false)} + > +
+ {popupMessage} +
+ +
+ +
+
diff --git a/src/lib/address/components/EditAddress.jsx b/src/lib/address/components/EditAddress.jsx index 6599a764..d9908726 100644 --- a/src/lib/address/components/EditAddress.jsx +++ b/src/lib/address/components/EditAddress.jsx @@ -44,6 +44,13 @@ const EditAddress = ({ id, defaultValues }) => { const [districts, setDistricts] = useState([]); const [subDistricts, setSubDistricts] = useState([]); const [tempAddress, setTempAddress] = useState(getValues('addressMap')); + 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 { addressMaps, @@ -56,48 +63,65 @@ const EditAddress = ({ id, defaultValues }) => { // Helper: cek apakah benar2 sudah PIN (bukan default center & ada addressMaps) const isPinned = useMemo(() => { - if ( - !selectedPosition || - typeof selectedPosition.lat !== 'number' || - typeof selectedPosition.lng !== 'number' - ) - return false; + 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' + typeof getDefaultCenter === "function" ? getDefaultCenter() : { lat: -6.2, lng: 106.816666 }; + const nearDefault = - Math.abs(selectedPosition.lat - dc.lat) < 1e-4 && - Math.abs(selectedPosition.lng - dc.lng) < 1e-4; + 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 && isPinned) { - setTempAddress(addressMaps); - setValue('addressMap', addressMaps); - } - if (isPinned && selectedPosition) { - 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, isPinned, setValue]); + }, [setSelectedPosition, setAddressMaps, getValues, setValue]); - // Isi ZIP/Prov dari detailAddress (JANGAN isi street) useEffect(() => { - if (Object.keys(detailAddress || {}).length > 0 && isPinned) { - setValue('zip', detailAddress.postalCode); - const selectedState = states.find( - (state) => - detailAddress?.province?.includes(state.label) || - state.label?.includes(detailAddress?.province) - ); - setValue('state', selectedState?.value); - // jangan override street: - // setValue('street', detailAddress?.street); + 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) }); + } } - }, [detailAddress, states, isPinned, setValue]); + }, [getValues, resetPin, setAddressMaps, setSelectedPosition]); + + useEffect(() => { const loadProfile = async () => { @@ -109,13 +133,29 @@ 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, setValue]); + // Isi ZIP/Prov dari detailAddress (JANGAN isi street) + useEffect(() => { + const zip = getValues("zip"); + const province = getValues("state"); + if (!zip && defaultValues?.zip) { + setValue("zip", defaultValues.zip); + } + + if (!getValues("state") && province) { + const selectedState = states.find( + (state) => + province.includes(state.label) || state.label.includes(province) + ); + if (selectedState) { + setValue("state", selectedState.value); + } + } + }, [states, setValue, getValues, defaultValues]); + useEffect(() => { const loadStates = async () => { let dataStates = await stateApi({ tempo: false }); @@ -150,7 +190,11 @@ const EditAddress = ({ id, defaultValues }) => { }, [watchState, setValue, getValues]); useEffect(() => { - if (Object.keys(detailAddress || {}).length > 0 && isPinned) { + if (!isPinned) return; + + if (getValues("city")) return; + + if (Object.keys(detailAddress || {}).length > 0) { const selectedCities = cities.find( (city) => @@ -165,9 +209,12 @@ const EditAddress = ({ id, defaultValues }) => { .toLowerCase() .includes(detailAddress?.district?.toLowerCase()) ); - setValue('city', selectedCities?.value); + + if (selectedCities) { + setValue("city", selectedCities.value); + } } - }, [cities, detailAddress, isPinned, setValue]); + }, [cities, detailAddress, isPinned, getValues, setValue]); const watchCity = watch('city'); useEffect(() => { @@ -189,7 +236,12 @@ const EditAddress = ({ id, defaultValues }) => { }, [watchCity, setValue, getValues]); useEffect(() => { - if (Object.keys(detailAddress || {}).length > 0 && isPinned) { + if (!isPinned) return; // skip kalau belum pin + + // jangan override kalau form sudah punya nilai district + if (getValues("district")) return; + + if (Object.keys(detailAddress || {}).length > 0) { const selectedDistrict = districts.find( (district) => detailAddress?.subDistrict @@ -199,9 +251,13 @@ const EditAddress = ({ id, defaultValues }) => { .toLowerCase() .includes(detailAddress?.subDistrict?.toLowerCase()) ); - setValue('district', selectedDistrict?.value); + + if (selectedDistrict) { + setValue("district", selectedDistrict.value); + } } - }, [districts, detailAddress, isPinned, setValue]); + }, [districts, detailAddress, isPinned, getValues, setValue]); + const watchDistrict = watch('district'); useEffect(() => { @@ -227,7 +283,12 @@ const EditAddress = ({ id, defaultValues }) => { }, [watchDistrict, setValue, getValues]); useEffect(() => { - if (Object.keys(detailAddress || {}).length > 0 && isPinned) { + if (!isPinned) return; // skip kalau belum pin + + // jangan override kalau form sudah punya nilai subDistrict + if (getValues("subDistrict")) return; + + if (Object.keys(detailAddress || {}).length > 0) { const selectedSubDistrict = subDistricts.find( (district) => detailAddress?.village @@ -237,9 +298,12 @@ const EditAddress = ({ id, defaultValues }) => { .toLowerCase() .includes(detailAddress?.village?.toLowerCase()) ); - setValue('subDistrict', selectedSubDistrict?.value); + + if (selectedSubDistrict) { + setValue("subDistrict", selectedSubDistrict.value); + } } - }, [subDistricts, detailAddress, isPinned, setValue]); + }, [subDistricts, detailAddress, isPinned, getValues, setValue]); useEffect(() => { if (id) { @@ -247,7 +311,52 @@ const EditAddress = ({ id, defaultValues }) => { } }, [id, setValue]); + useEffect(() => { + const currentCity = cities.find((c) => c.value === watch("city"))?.label || ""; + + let normalized = currentCity.toLowerCase().trim(); + + const parts = normalized.split(" "); + + if (parts.length >= 3) { + // hapus prefix kabupaten/kota kalau ada + normalized = normalized + .replace(/^kabupaten\s+/i, "") + .replace(/^kota\s+/i, "") + .trim(); + } + + setSelectedCityName(normalized); + }, [watch("city"), cities]); + // console.log(defaultValues); + + // console.log(selectedCityName, '=', detailAddress?.district); 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){ + if (selectedCityName && selectedCityName !== detailAddress?.district?.toLowerCase()) { + 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, @@ -257,7 +366,7 @@ const EditAddress = ({ id, defaultValues }) => { sub_district_id: parseInt(values.subDistrict, 10), }; - // kirim koordinat + address_map + use_pin HANYA jika sudah PIN + if (isPinned) { data.longtitude = selectedPosition?.lng; data.latitude = selectedPosition?.lat; @@ -324,7 +433,7 @@ const EditAddress = ({ id, defaultValues }) => { const dataProfile = await addressApi({ id: auth.partnerId }); console.log('ini adalah', dataProfile); }; - + // console.log('ini adalah', detailAddress); return ( <> { >
+ setShowValidationPopup(false)} + > +
+ {popupMessage} +
+ +
+ +
+
@@ -355,7 +482,7 @@ const EditAddress = ({ id, defaultValues }) => {
- {tempAddress ? ( + {addressMaps ? (
- {tempAddress} + {addressMaps}
) : (