|
@@ -92,6 +92,27 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
|
|
+ <div v-if="missingCoords" class="q-mb-md">
|
|
|
|
|
+ <q-banner rounded dense class="bg-orange-1 text-orange-9 q-mb-sm">
|
|
|
|
|
+ <template #avatar>
|
|
|
|
|
+ <q-icon name="mdi-map-marker-off" color="orange-7" />
|
|
|
|
|
+ </template>
|
|
|
|
|
+ {{ $t('profile.address.missing_coords') }}
|
|
|
|
|
+ </q-banner>
|
|
|
|
|
+ <q-btn
|
|
|
|
|
+ unelevated
|
|
|
|
|
+ rounded
|
|
|
|
|
+ no-caps
|
|
|
|
|
+ outline
|
|
|
|
|
+ color="primary"
|
|
|
|
|
+ icon="mdi-map-marker"
|
|
|
|
|
+ class="full-width"
|
|
|
|
|
+ :label="$t('profile.address.update_on_map')"
|
|
|
|
|
+ :loading="geocodingCep"
|
|
|
|
|
+ @click="openMapDialog"
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
<q-btn
|
|
<q-btn
|
|
|
unelevated
|
|
unelevated
|
|
|
rounded
|
|
rounded
|
|
@@ -114,12 +135,14 @@
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script setup>
|
|
<script setup>
|
|
|
-import { ref, onMounted } from 'vue';
|
|
|
|
|
|
|
+import { ref, computed, onMounted } from 'vue';
|
|
|
import { useDialogPluginComponent, useQuasar } from 'quasar';
|
|
import { useDialogPluginComponent, useQuasar } from 'quasar';
|
|
|
import { searchAddressByCEP, updateAddress, createAddress } from 'src/api/address';
|
|
import { searchAddressByCEP, updateAddress, createAddress } from 'src/api/address';
|
|
|
import { userStore } from 'src/stores/user';
|
|
import { userStore } from 'src/stores/user';
|
|
|
import { useFormUpdateTracker } from 'src/composables/useFormUpdateTracker';
|
|
import { useFormUpdateTracker } from 'src/composables/useFormUpdateTracker';
|
|
|
|
|
+import { useGeocodingApi } from 'src/composables/useGeocodingApi';
|
|
|
import { useI18n } from 'vue-i18n';
|
|
import { useI18n } from 'vue-i18n';
|
|
|
|
|
+import LocationMapDialog from 'src/components/shared/LocationMapDialog.vue';
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
const props = defineProps({
|
|
|
isEditing: {
|
|
isEditing: {
|
|
@@ -139,6 +162,7 @@ const $q = useQuasar();
|
|
|
const { t } = useI18n();
|
|
const { t } = useI18n();
|
|
|
const user = userStore();
|
|
const user = userStore();
|
|
|
const clientId = user.user.client.id;
|
|
const clientId = user.user.client.id;
|
|
|
|
|
+const { geocodeFullAddress } = useGeocodingApi();
|
|
|
|
|
|
|
|
const initialFormData = {
|
|
const initialFormData = {
|
|
|
zip_code: '',
|
|
zip_code: '',
|
|
@@ -153,6 +177,8 @@ const initialFormData = {
|
|
|
source: 'client',
|
|
source: 'client',
|
|
|
source_id: clientId,
|
|
source_id: clientId,
|
|
|
address_type: 'home',
|
|
address_type: 'home',
|
|
|
|
|
+ latitude: null,
|
|
|
|
|
+ longitude: null,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
const { form, hasUpdatedFields, getUpdatedFields, setUpdateFormAsOriginal } =
|
|
const { form, hasUpdatedFields, getUpdatedFields, setUpdateFormAsOriginal } =
|
|
@@ -161,6 +187,11 @@ const { form, hasUpdatedFields, getUpdatedFields, setUpdateFormAsOriginal } =
|
|
|
const loadingCep = ref(false);
|
|
const loadingCep = ref(false);
|
|
|
const saving = ref(false);
|
|
const saving = ref(false);
|
|
|
const addressId = ref(null);
|
|
const addressId = ref(null);
|
|
|
|
|
+const geocodingCep = ref(false);
|
|
|
|
|
+
|
|
|
|
|
+const missingCoords = computed(() =>
|
|
|
|
|
+ props.isEditing && form.latitude == null && form.longitude == null
|
|
|
|
|
+);
|
|
|
|
|
|
|
|
const addressTypes = [
|
|
const addressTypes = [
|
|
|
{ value: 'home', label: 'profile.address.type.home', icon: 'mdi-home-outline' },
|
|
{ value: 'home', label: 'profile.address.type.home', icon: 'mdi-home-outline' },
|
|
@@ -180,6 +211,8 @@ const onCepChange = async (val) => {
|
|
|
form.state_id = data.state_id;
|
|
form.state_id = data.state_id;
|
|
|
form.city = data.city;
|
|
form.city = data.city;
|
|
|
form.state = data.state;
|
|
form.state = data.state;
|
|
|
|
|
+ form.latitude = null;
|
|
|
|
|
+ form.longitude = null;
|
|
|
} else {
|
|
} else {
|
|
|
$q.notify({ type: 'negative', message: t('profile.address.cep_not_found') });
|
|
$q.notify({ type: 'negative', message: t('profile.address.cep_not_found') });
|
|
|
}
|
|
}
|
|
@@ -189,6 +222,64 @@ const onCepChange = async (val) => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+const openMapDialog = async () => {
|
|
|
|
|
+ let initialLat = null;
|
|
|
|
|
+ let initialLng = null;
|
|
|
|
|
+
|
|
|
|
|
+ const hasAddress = form.address || form.zip_code;
|
|
|
|
|
+ if (hasAddress) {
|
|
|
|
|
+ geocodingCep.value = true;
|
|
|
|
|
+ try {
|
|
|
|
|
+ const geo = await geocodeFullAddress({
|
|
|
|
|
+ address: form.address,
|
|
|
|
|
+ number: form.number,
|
|
|
|
|
+ district: form.district,
|
|
|
|
|
+ zip_code: form.zip_code,
|
|
|
|
|
+ city: form.city?.name,
|
|
|
|
|
+ state: form.state?.name,
|
|
|
|
|
+ });
|
|
|
|
|
+ if (geo) {
|
|
|
|
|
+ initialLat = geo.lat;
|
|
|
|
|
+ initialLng = geo.lng;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ // fallback to default
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ geocodingCep.value = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ const dialogProps = initialLat !== null
|
|
|
|
|
+ ? { initialLat, initialLng }
|
|
|
|
|
+ : {};
|
|
|
|
|
+
|
|
|
|
|
+ $q.dialog({
|
|
|
|
|
+ component: LocationMapDialog,
|
|
|
|
|
+ componentProps: dialogProps,
|
|
|
|
|
+ }).onOk(async (geoData) => {
|
|
|
|
|
+ form.latitude = geoData.lat;
|
|
|
|
|
+ form.longitude = geoData.lng;
|
|
|
|
|
+ form.address = geoData.address || form.address;
|
|
|
|
|
+ form.number = geoData.number || form.number;
|
|
|
|
|
+ form.district = geoData.district || form.district;
|
|
|
|
|
+
|
|
|
|
|
+ if (geoData.zip_code) {
|
|
|
|
|
+ form.zip_code = geoData.zip_code.replace(/\D/g, '');
|
|
|
|
|
+ try {
|
|
|
|
|
+ const addressData = await searchAddressByCEP(form.zip_code);
|
|
|
|
|
+ if (addressData) {
|
|
|
|
|
+ form.city_id = addressData.city_id;
|
|
|
|
|
+ form.state_id = addressData.state_id;
|
|
|
|
|
+ form.city = addressData.city;
|
|
|
|
|
+ form.state = addressData.state;
|
|
|
|
|
+ }
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ // mantém cidade/estado atual se lookup falhar
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
const save = async () => {
|
|
const save = async () => {
|
|
|
saving.value = true;
|
|
saving.value = true;
|
|
|
try {
|
|
try {
|
|
@@ -228,6 +319,8 @@ onMounted(() => {
|
|
|
source: 'client',
|
|
source: 'client',
|
|
|
source_id: clientId,
|
|
source_id: clientId,
|
|
|
address_type: props.addressData.address_type || 'home',
|
|
address_type: props.addressData.address_type || 'home',
|
|
|
|
|
+ latitude: props.addressData.latitude ?? null,
|
|
|
|
|
+ longitude: props.addressData.longitude ?? null,
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
Object.assign(form, initialData);
|
|
Object.assign(form, initialData);
|