diff --git a/src/views/pages/Property/CreateProperty.vue b/src/views/pages/Property/CreateProperty.vue index 389f31f..922e25e 100644 --- a/src/views/pages/Property/CreateProperty.vue +++ b/src/views/pages/Property/CreateProperty.vue @@ -22,6 +22,7 @@ import ToggleSwitch from 'primevue/toggleswitch'; import Button from 'primevue/button'; import Tag from 'primevue/tag'; import SelectButton from 'primevue/selectbutton'; +import ProgressSpinner from 'primevue/progressspinner'; const toast = useToast(); const route = useRoute(); @@ -37,6 +38,7 @@ const mainImageUrlsWithData = ref([]); const documentUrlsWithData = ref([]); const editMode = ref(false); const isSaving = ref(false); +const pageLoading = ref(true); // Registration Date Fields (Jalali: Year, Month, Day) const regYear = ref(''); @@ -130,8 +132,8 @@ const mapMarker = ref([]); const easting = ref(''); const northing = ref(''); const zone = ref(''); -const lat = ref(); -const long = ref(); +const lat = ref(null); +const long = ref(null); // Auto-generated property name: ownerName + شماره اصلی/شماره فرعی const computedPropertyName = computed(() => { @@ -162,6 +164,11 @@ const setCoordinatesAndUTM = (latitude, longitude, utmE = null, utmN = null, utm if (latitude !== null && longitude !== null && !isNaN(latitude) && !isNaN(longitude)) { const numLat = Number(latitude); const numLon = Number(longitude); + + if (lat.value === numLat && long.value === numLon && mapMarker.value?.[0] === numLat && mapMarker.value?.[1] === numLon) { + return; + } + lat.value = numLat; long.value = numLon; mapMarker.value = [numLat, numLon]; @@ -182,12 +189,6 @@ const setCoordinatesAndUTM = (latitude, longitude, utmE = null, utmN = null, utm zone.value = String(utm.zoneNumber); } } - - nextTick(() => { - if (mapRef.value && typeof mapRef.value.setCenterAndMarker === 'function') { - mapRef.value.setCenterAndMarker(numLat, numLon); - } - }); } }; @@ -249,20 +250,17 @@ let geoCodeDebounceTimeout = null; * - First 6 digits → Easting (e.g. 538136) * - Next 7 digits → Northing (e.g. 3950135) * - Last 2 digits → UTM Zone (e.g. 39 for most of Iran) - * If the code doesn't match the 15-digit pattern, falls back to zone 39. */ const parseNationalGeoCode = (code) => { if (!code) return null; - const digits = toEnglishDigits(String(code)).replace(/\D/g, ''); // strip non-digits + const digits = toEnglishDigits(String(code)).replace(/\D/g, ''); if (digits.length === 15) { - // Standard JAM: 6 easting + 7 northing + 2 zone const e = parseInt(digits.slice(0, 6), 10); const n = parseInt(digits.slice(6, 13), 10); const z = parseInt(digits.slice(13, 15), 10); return { easting: e, northing: n, zone: z }; } if (digits.length === 13) { - // Some codes omit the zone (older format) — assume zone 39 for Iran const e = parseInt(digits.slice(0, 6), 10); const n = parseInt(digits.slice(6, 13), 10); return { easting: e, northing: n, zone: 39 }; @@ -272,7 +270,6 @@ const parseNationalGeoCode = (code) => { const applyNationalGeoCode = (val) => { if (!val) return; - // 1. Try parsing as a JAM UTM code (15-digit or 13-digit) const parsed = parseNationalGeoCode(val); if (parsed) { try { @@ -285,7 +282,6 @@ const applyNationalGeoCode = (val) => { console.error('UTM conversion failed:', err); } } - // 2. Fallback: treat as lat,lon text or geocode search via map if (mapRef.value && typeof mapRef.value.searchLocation === 'function') { mapRef.value.searchLocation(val); } @@ -297,41 +293,41 @@ const handleNationalGeoCodeChange = () => { const digits = toEnglishDigits(val).replace(/\D/g, ''); - // Fire immediately once a full 15-digit JAM code is complete — no waiting if (digits.length === 15) { if (geoCodeDebounceTimeout) clearTimeout(geoCodeDebounceTimeout); applyNationalGeoCode(val); return; } - // For partial / non-standard input, debounce to avoid firing on every keystroke if (geoCodeDebounceTimeout) clearTimeout(geoCodeDebounceTimeout); geoCodeDebounceTimeout = setTimeout(() => applyNationalGeoCode(val), 600); }; // Auto sync category when propertyType changes watch(() => property.value.propertyType, (newType) => { + if (pageLoading.value || editMode.value) return; if (!categories.value || categories.value.length === 0) return; let targetCatName = newType === 'Land' ? 'Lot' : 'Building'; let matchedCategory = categories.value.find(c => c.englishName === targetCatName || c.nativeName === (newType === 'Land' ? 'زمین' : 'ساختمان')); - if (matchedCategory) { + if (matchedCategory && !property.value.category) { property.value.category = matchedCategory._id; handleSelectCategory(matchedCategory._id); } }); const initializeFeatureDefaultValues = () => { - featuresValues.value = {}; if (features.value && Array.isArray(features.value)) { features.value.forEach((feat) => { const definition = feat.definition || feat; if (definition && definition._id) { - if (definition.type === 'boolean') { - featuresValues.value[definition._id] = false; - } else if (definition.type === 'multiselect') { - featuresValues.value[definition._id] = []; - } else { - featuresValues.value[definition._id] = null; + if (featuresValues.value[definition._id] === undefined) { + if (definition.type === 'boolean' || definition.featureType === 'boolean') { + featuresValues.value[definition._id] = false; + } else if (definition.type === 'multiselect') { + featuresValues.value[definition._id] = []; + } else { + featuresValues.value[definition._id] = null; + } } } }); @@ -403,44 +399,23 @@ const fillImagesArray = (urlsArray) => { }; const handleSelectCategory = (catId) => { - selectedCategory.value = categories.value.find((c) => c._id === catId); + selectedCategory.value = categories.value.find((c) => c._id === catId) || null; if (selectedCategory.value) { features.value = selectedCategory.value.features || []; initializeFeatureDefaultValues(); } else { features.value = []; - featuresValues.value = {}; - } -}; - -const onMainImageUploaded = (res) => { - if (res && res.url) { - mainImageUrlsWithData.value.push({ - name: res.originalName || 'image', - url: res.url, - progress: 100 - }); - } -}; - -const onDocumentUploaded = (res) => { - if (res && res.url) { - documentUrlsWithData.value.push({ - name: res.originalName || 'document', - url: res.url, - progress: 100 - }); } }; const getStatusSeverity = (st) => { if (!st) return 'secondary'; - // Use the color field from the API; fall back to secondary const valid = ['success', 'info', 'warn', 'danger', 'secondary', 'contrast']; return valid.includes(st.color) ? st.color : 'secondary'; }; onBeforeMount(async () => { + pageLoading.value = true; try { const [fetchedCategories, fetchedStatuses] = await Promise.all([ CategoryService.getAllCategories(), @@ -454,8 +429,29 @@ onBeforeMount(async () => { editMode.value = true; const data = await PropertyService.getProperty(propId); if (data) { - property.value = { ...property.value, ...data }; - featuresValues.value = extractFeaturesValues(data.features); + const catId = typeof data.category === 'object' ? data.category?._id : data.category; + const statusId = typeof data.status === 'object' ? data.status?._id : data.status; + + property.value = { + ...property.value, + ...data, + category: catId || null, + status: statusId || null, + dong: Number(data.dong) || 6, + area: data.area !== undefined && data.area !== null ? Number(data.area) : null + }; + + if (catId) { + selectedCategory.value = categories.value.find((x) => x._id === catId); + if (selectedCategory.value) { + features.value = selectedCategory.value.features || []; + } + } + + if (data.features && Array.isArray(data.features)) { + featuresValues.value = extractFeaturesValues(data.features); + } + initializeFeatureDefaultValues(); // 1. Parse registrationDate into Jalali Year, Month, Day if (data.registrationDate) { @@ -499,14 +495,8 @@ onBeforeMount(async () => { mainImageUrlsWithData.value = fillImagesArray(mainImages); documentUrlsWithData.value = fillImagesArray(docs); - - if (data.category) { - const catId = typeof data.category === 'object' ? data.category._id : data.category; - selectedCategory.value = categories.value.find((x) => x._id === catId); - if (selectedCategory.value) { - features.value = selectedCategory.value.features || []; - } - } + } else { + toast.add({ severity: 'error', summary: 'خطا', detail: 'ملک مورد نظر یافت نشد.', life: 4000 }); } } else { let matchedCat = categories.value.find(c => c.englishName === 'Lot' || c.nativeName === 'زمین'); @@ -517,11 +507,13 @@ onBeforeMount(async () => { } } catch (err) { console.error('Initialization error:', err); + toast.add({ severity: 'error', summary: 'خطا', detail: 'خطا در دریافت اطلاعات ملک از سرور', life: 4000 }); + } finally { + pageLoading.value = false; } }); const saveProperty = async () => { - // Auto-set name from ownerName + mainNumber/subNumber property.value.name = computedPropertyName.value || 'ملک جدید'; if (!property.value.nationalGeoCode?.trim()) { @@ -529,7 +521,6 @@ const saveProperty = async () => { return; } - // Convert Jalali date to standard Date object if (regYear.value && regMonth.value && regDay.value) { const dObj = jalaliToDate(regYear.value, regMonth.value, regDay.value); property.value.registrationDate = dObj ? dObj.toISOString() : null; @@ -611,14 +602,21 @@ const saveProperty = async () => { severity="primary" rounded :loading="isSaving" + :disabled="pageLoading" @click="saveProperty" class="px-5 font-semibold" /> + +
+ + در حال دریافت و بارگذاری اطلاعات ملک از پایگاه داده... +
+ -
+
@@ -642,101 +640,96 @@ const saveProperty = async () => { />
- +
- -
+
+ + الزامی +
+
-
+ + شامل ۶ رقم طول + ۷ رقم عرض + ۲ رقم زون UTM +
- +
- +
- +
- +
- +
- +
-
- -
- -
- -
- -
- -
- -
+
+ + +
+
+
- -
-