Pārlūkot izejas kodu

feat: permite usar localizacao exata no momento do cadastro

Gustavo Mantovani 1 nedēļu atpakaļ
vecāks
revīzija
fb124c4d66

+ 73 - 0
src/components/login/LoginStep3Panel.vue

@@ -63,6 +63,16 @@
       />
     </div>
 
+    <q-btn
+      class="full-width q-mb-md"
+      color="primary-button"
+      padding="8px 12px"
+      rounded
+      :label="$t('auth.use_location')"
+      :loading="loadingLocation"
+      @click="useLocation"
+    />
+
     <div>
       <div class="text-text">
         <span class="font14 fontbold">{{ $t("common.terms.address") }}</span>
@@ -261,9 +271,13 @@
 
 <script setup>
 import axios from "axios";
+import { useQuasar } from "quasar";
 import { ref } from "vue";
+import { useI18n } from "vue-i18n";
 
 import { useInputRules } from "src/composables/useInputRules";
+import { useGeolocation } from "src/composables/useGeolocation";
+import LocationMapDialog from "src/components/shared/LocationMapDialog.vue";
 
 const form = defineModel({
   required: true,
@@ -271,8 +285,12 @@ const form = defineModel({
 });
 
 const { inputRules } = useInputRules();
+const $q = useQuasar();
+const { t } = useI18n();
+const { requestPermission, getCurrentPosition } = useGeolocation();
 
 const loadingCep = ref(false);
+const loadingLocation = ref(false);
 
 const addressTypes = [
   {
@@ -330,6 +348,9 @@ const fetchCep = async (rawCep) => {
 const onCepChange = (val) => {
   const cleaned = val ? val.replace(/\D/g, "") : "";
 
+  form.value.latitude = null;
+  form.value.longitude = null;
+
   if (cleaned.length === 8) {
     fetchCep(val);
   } else {
@@ -339,4 +360,56 @@ const onCepChange = (val) => {
     form.value.state = "";
   }
 };
+
+const showCurrentLocationOnMap = ({ lat, lng }) => {
+  $q.dialog({
+    component: LocationMapDialog,
+    componentProps: {
+      initialLat: lat,
+      initialLng: lng,
+    },
+  }).onOk((geoData) => {
+    form.value.zip_code = geoData.zip_code ?? "";
+    form.value.address = geoData.address ?? "";
+    form.value.number = geoData.number ?? "";
+    form.value.district = geoData.district ?? "";
+    form.value.city = geoData.city ?? "";
+    form.value.state = geoData.state ?? "";
+    form.value.latitude = geoData.lat;
+    form.value.longitude = geoData.lng;
+  });
+};
+
+const useLocation = async () => {
+  loadingLocation.value = true;
+
+  try {
+    const granted = await requestPermission();
+
+    if (!granted) {
+      $q.notify({
+        message: t("auth.location_permission_denied"),
+        type: "warning",
+      });
+
+      return;
+    }
+
+    const position = await getCurrentPosition();
+
+    showCurrentLocationOnMap(position);
+  } catch (err) {
+    const isPermissionError =
+      err?.code === 1 || err?.message?.toLowerCase().includes("denied");
+
+    $q.notify({
+      message: isPermissionError
+        ? t("auth.location_permission_denied")
+        : t("auth.location_error"),
+      type: "warning",
+    });
+  } finally {
+    loadingLocation.value = false;
+  }
+};
 </script>

+ 426 - 159
src/components/profile/ProfileAddressFormDialog.vue

@@ -1,76 +1,181 @@
 <template>
-  <q-dialog ref="dialogRef" persistent maximized transition-show="slide-left" transition-hide="slide-right">
+  <q-dialog
+    ref="dialogRef"
+    maximized
+    persistent
+    transition-hide="slide-right"
+    transition-show="slide-left"
+  >
     <div class="bg-page full-height no-shadow">
-      <div class="row items-center q-px-md q-pt-md q-pb-sm bg-white shadow-profile bg-surface">
-        <q-btn v-close-popup icon="mdi-chevron-left" flat round dense color="primary" />
+      <div
+        class="row items-center q-px-md q-pt-md q-pb-sm bg-white shadow-profile bg-surface"
+      >
+        <q-btn
+          v-close-popup
+          color="primary"
+          dense
+          flat
+          icon="mdi-chevron-left"
+          round
+        />
+
         <q-space />
-        <span class="text-subtitle1 text-primary font16 fontbold gradient-diarista">{{ $t('profile.address.title') }}</span>
+
+        <span
+          class="gradient-diarista text-subtitle1 text-primary font16 fontbold"
+        >
+          {{ $t("profile.address.title") }}
+        </span>
+
         <q-space />
+
         <div style="width: 32px"></div>
       </div>
+
       <div class="col column q-px-lg q-pt-lg overflow-auto">
-        <span class="gradient-diarista q-mb-xs font16 fontbold gradient-diarista">{{ isEditing ? $t('profile.address.edit_address') : $t('profile.address.add_new_address') }}</span>
-        <span class="text-grey-7 q-mb-lg font12 fontregular" style="line-height: 1.3;">{{ isEditing ? $t('profile.address.manage_addresses') : $t('profile.address.add_new_address_label') }}</span>
+        <span class="gradient-diarista q-mb-xs font16 fontbold">
+          {{
+            isEditing
+              ? $t("profile.address.edit_address")
+              : $t("profile.address.add_new_address")
+          }}
+        </span>
+
+        <span
+          class="text-grey-7 q-mb-lg font12 fontregular"
+          style="line-height: 1.3"
+        >
+          {{
+            isEditing
+              ? $t("profile.address.manage_addresses")
+              : $t("profile.address.add_new_address_label")
+          }}
+        </span>
       </div>
+
       <q-card-section class="col">
         <div class="text-text">
-          <q-card class="q-pa-lg bg-white shadow-card" style="border-radius: 25px;" :flat="false">
-            <div class="text-text q-mb-xs font12 fontbold">{{ $t('profile.address.cep') }}</div>
+          <q-card
+            class="q-pa-lg bg-white shadow-card"
+            style="border-radius: 25px"
+            :flat="false"
+          >
+            <div class="text-text q-mb-xs font12 fontbold">
+              {{ $t("profile.address.cep") }}
+            </div>
+
             <q-input
               v-model="form.zip_code"
-              input-class="text-text"
-              outlined
+              class="q-mb-md"
               dense
+              input-class="text-text"
               mask="#####-###"
-              unmasked-value
+              outlined
               placeholder="00000-000"
-              class="q-mb-md"
+              unmasked-value
               @update:model-value="onCepChange"
             >
               <template #append>
-                <q-spinner v-if="loadingCep" size="xs" color="primary" />
+                <q-spinner
+                  v-if="loadingCep"
+                  color="primary"
+                  size="xs"
+                />
               </template>
             </q-input>
 
+            <q-btn
+              class="full-width q-mb-md"
+              color="primary-button"
+              padding="8px 12px"
+              rounded
+              :label="$t('auth.use_location')"
+              :loading="loadingLocation"
+              @click="useLocation"
+            />
+
+            <div class="q-mb-xs text-text font12 fontbold">
+              {{ $t("profile.address.address_label") }}
+            </div>
 
-            <div class="q-mb-xs text-text font12 fontbold">{{ $t('profile.address.address_label') }}</div>
             <q-input
               v-model="form.address"
-              outlined
-              dense
               class="q-mb-md"
+              dense
               input-class="text-text"
+              outlined
               :placeholder="$t('profile.address.address_placeholder')"
             />
 
             <div class="row q-col-gutter-sm q-mb-md">
               <div class="col-4">
-                <div class="q-mb-xs text-text font12 fontbold">{{ $t('profile.address.number') }}</div>
-                <q-input v-model="form.number" outlined dense input-class="text-text" placeholder="0000" />
+                <div class="q-mb-xs text-text font12 fontbold">
+                  {{ $t("profile.address.number") }}
+                </div>
+
+                <q-input
+                  v-model="form.number"
+                  dense
+                  input-class="text-text"
+                  outlined
+                  placeholder="0000"
+                />
               </div>
+
               <div class="col-8">
-                <div class="q-mb-xs text-text font12 fontbold">{{ $t('profile.address.complement') }}</div>
+                <div class="q-mb-xs text-text font12 fontbold">
+                  {{ $t("profile.address.complement") }}
+                </div>
+
                 <q-input
                   v-model="form.complement"
-                  outlined
                   dense
                   input-class="text-text"
+                  outlined
                   :placeholder="$t('profile.address.complement_placeholder')"
                 />
               </div>
             </div>
 
-            <div class="q-mb-xs text-text font12 fontbold">{{ $t('profile.address.district_label') }}</div>
-            <q-input v-model="form.district" outlined dense class="q-mb-md" input-class="text-text" />
+            <div class="q-mb-xs text-text font12 fontbold">
+              {{ $t("profile.address.district_label") }}
+            </div>
+
+            <q-input
+              v-model="form.district"
+              class="q-mb-md"
+              dense
+              input-class="text-text"
+              outlined
+            />
 
             <div class="row q-col-gutter-sm q-mb-lg">
               <div class="col-8">
-                <div class="q-mb-xs text-text font12 fontbold">{{ $t('profile.address.city_label') }}</div>
-                <q-input :model-value="form.city?.name" readonly outlined dense input-class="text-text" />
+                <div class="q-mb-xs text-text font12 fontbold">
+                  {{ $t("profile.address.city_label") }}
+                </div>
+
+                <q-input
+                  dense
+                  input-class="text-text"
+                  outlined
+                  readonly
+                  :model-value="form.city?.name"
+                />
               </div>
+
               <div class="col-4">
-                <div class="q-mb-xs text-text font12 fontbold">{{ $t('profile.address.state_label') }}</div>
-                <q-input :model-value="form.state?.name" readonly outlined dense input-class="text-text" />
+                <div class="q-mb-xs text-text font12 fontbold">
+                  {{ $t("profile.address.state_label") }}
+                </div>
+
+                <q-input
+                  dense
+                  input-class="text-text"
+                  outlined
+                  readonly
+                  :model-value="form.state?.name"
+                />
               </div>
             </div>
 
@@ -79,13 +184,13 @@
                 <q-chip
                   v-for="type in addressTypes"
                   :key="type.value"
+                  :icon="type.icon"
+                  :icon-selected="type.icon"
+                  :outline="form.address_type !== type.value"
                   :selected="form.address_type === type.value"
                   clickable
                   color="primary"
-                  :outline="form.address_type !== type.value"
                   text-color="surface"
-                  :icon="type.icon"
-                  :icon-selected="type.icon"
                   @click="form.address_type = type.value"
                 >
                   {{ $t(type.label) }}
@@ -93,21 +198,33 @@
               </div>
             </div>
 
-            <div v-if="missingCoords" class="q-mb-md">
-              <q-banner rounded dense class="bg-orange-1 text-orange-9 q-mb-sm">
+            <div
+              v-if="missingCoords"
+              class="q-mb-md"
+            >
+              <q-banner
+                class="bg-orange-1 text-orange-9 q-mb-sm"
+                dense
+                rounded
+              >
                 <template #avatar>
-                  <q-icon name="mdi-map-marker-off" color="orange-7" />
+                  <q-icon
+                    color="orange-7"
+                    name="mdi-map-marker-off"
+                  />
                 </template>
-                {{ $t('profile.address.missing_coords') }}
+
+                {{ $t("profile.address.missing_coords") }}
               </q-banner>
+
               <q-btn
-                unelevated
-                rounded
-                no-caps
-                outline
+                class="full-width"
                 color="primary"
                 icon="mdi-map-marker"
-                class="full-width"
+                no-caps
+                outline
+                rounded
+                unelevated
                 :label="$t('profile.address.update_on_map')"
                 :loading="geocodingCep"
                 @click="openMapDialog"
@@ -115,19 +232,20 @@
             </div>
 
             <q-btn
-              unelevated
-              rounded
-              no-caps
-              color="primary"
               class="full-width q-py-md"
+              color="primary"
+              no-caps
               padding="8px 12px"
+              rounded
+              unelevated
+              :disable="!hasUpdatedFields"
               :label="$t('common.actions.save')"
               :loading="saving"
-              :disable="!hasUpdatedFields"
               @click="save"
             />
           </q-card>
         </div>
+
         <div class="q-pb-xl"></div>
       </q-card-section>
     </div>
@@ -135,90 +253,128 @@
 </template>
 
 <script setup>
-import { ref, computed, onMounted } from 'vue';
-import { useDialogPluginComponent, useQuasar } from 'quasar';
-import { searchAddressByCEP, updateAddress, createAddress } from 'src/api/address';
-import { userStore } from 'src/stores/user';
-import { useFormUpdateTracker } from 'src/composables/useFormUpdateTracker';
-import { useGeocodingApi } from 'src/composables/useGeocodingApi';
-import { useI18n } from 'vue-i18n';
-import LocationMapDialog from 'src/components/shared/LocationMapDialog.vue';
+import { computed, onMounted, ref } from "vue";
+
+import {
+  createAddress,
+  searchAddressByCEP,
+  updateAddress,
+} from "src/api/address";
+
+import { useDialogPluginComponent, useQuasar } from "quasar";
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
+import { useGeocodingApi } from "src/composables/useGeocodingApi";
+import { useGeolocation } from "src/composables/useGeolocation";
+import { useI18n } from "vue-i18n";
+import { userStore } from "src/stores/user";
+
+import LocationMapDialog from "src/components/shared/LocationMapDialog.vue";
+
+defineEmits([...useDialogPluginComponent.emits]);
 
 const props = defineProps({
-  isEditing: {
-    type: Boolean,
-    default: false,
-  },
   addressData: {
-    type: Object,
     default: null,
+    type: Object,
+  },
+  isEditing: {
+    default: false,
+    type: Boolean,
   },
 });
 
-defineEmits([...useDialogPluginComponent.emits]);
-
-const { dialogRef, onDialogOK } = useDialogPluginComponent();
 const $q = useQuasar();
 const { t } = useI18n();
+const { dialogRef, onDialogOK } = useDialogPluginComponent();
+const { geocodeFullAddress } = useGeocodingApi();
+const { getCurrentPosition, requestPermission } = useGeolocation();
+
 const user = userStore();
+
+const addressId = ref(null);
+const geocodingCep = ref(false);
+const loadingCep = ref(false);
+const loadingLocation = ref(false);
+const saving = ref(false);
+
+const missingCoords = computed(
+  () =>
+    props.isEditing &&
+    form.latitude == null &&
+    form.longitude == null,
+);
+
 const clientId = user.user.client.id;
-const { geocodeFullAddress } = useGeocodingApi();
 
 const initialFormData = {
-  zip_code: '',
-  address: '',
-  number: '',
-  complement: '',
-  district: '',
-  city_id: null,
-  state_id: null,
+  address: "",
+  address_type: "home",
   city: null,
-  state: null,
-  source: 'client',
-  source_id: clientId,
-  address_type: 'home',
+  city_id: null,
+  complement: "",
+  district: "",
   latitude: null,
   longitude: null,
+  number: "",
+  source: "client",
+  source_id: clientId,
+  state: null,
+  state_id: null,
+  zip_code: "",
 };
 
-const { form, hasUpdatedFields, getUpdatedFields, setUpdateFormAsOriginal } =
-  useFormUpdateTracker(initialFormData);
-
-const loadingCep = ref(false);
-const saving = ref(false);
-const addressId = ref(null);
-const geocodingCep = ref(false);
-
-const missingCoords = computed(() =>
-  props.isEditing && form.latitude == null && form.longitude == null
-);
+const {
+  form,
+  getUpdatedFields,
+  hasUpdatedFields,
+  setUpdateFormAsOriginal,
+} = useFormUpdateTracker(initialFormData);
 
 const addressTypes = [
-  { value: 'home', label: 'profile.address.type.home', icon: 'mdi-home-outline' },
-  { value: 'commercial', label: 'profile.address.type.commercial', icon: 'mdi-briefcase-variant-outline' },
-  { value: 'other', label: 'profile.address.type.other', icon: 'mdi-map-marker-outline' },
+  {
+    icon: "mdi-home-outline",
+    label: "profile.address.type.home",
+    value: "home",
+  },
+  {
+    icon: "mdi-briefcase-variant-outline",
+    label: "profile.address.type.commercial",
+    value: "commercial",
+  },
+  {
+    icon: "mdi-map-marker-outline",
+    label: "profile.address.type.other",
+    value: "other",
+  },
 ];
 
 const onCepChange = async (val) => {
-  if (val?.length === 8) {
-    loadingCep.value = true;
-    try {
-      const data = await searchAddressByCEP(val);
-      if (data) {
-        form.address = data.address;
-        form.district = data.district;
-        form.city_id = data.city_id;
-        form.state_id = data.state_id;
-        form.city = data.city;
-        form.state = data.state;
-        form.latitude = null;
-        form.longitude = null;
-      } else {
-        $q.notify({ type: 'negative', message: t('profile.address.cep_not_found') });
-      }
-    } finally {
-      loadingCep.value = false;
+  if (val?.length !== 8) {
+    return;
+  }
+
+  loadingCep.value = true;
+
+  try {
+    const data = await searchAddressByCEP(val);
+
+    if (data) {
+      form.address = data.address;
+      form.city = data.city;
+      form.city_id = data.city_id;
+      form.district = data.district;
+      form.latitude = null;
+      form.longitude = null;
+      form.state = data.state;
+      form.state_id = data.state_id;
+    } else {
+      $q.notify({
+        message: t("profile.address.cep_not_found"),
+        type: "negative",
+      });
     }
+  } finally {
+    loadingCep.value = false;
   }
 };
 
@@ -228,74 +384,181 @@ const openMapDialog = async () => {
 
   const hasAddress = form.address || form.zip_code;
 
-  $q.notify({ color: 'indigo-9', textColor: 'white', position: 'top', message: '[ADDR] openMapDialog iniciado', caption: `hasAddress: ${hasAddress}\naddress: ${form.address}\nzip_code: ${form.zip_code}\nlat: ${form.latitude}\nlng: ${form.longitude}`, timeout: 8000, multiLine: true });
+  $q.notify({
+    caption: `hasAddress: ${hasAddress}\naddress: ${form.address}\nzip_code: ${form.zip_code}\nlat: ${form.latitude}\nlng: ${form.longitude}`,
+    color: "indigo-9",
+    message: "[ADDR] openMapDialog iniciado",
+    multiLine: true,
+    position: "top",
+    textColor: "white",
+    timeout: 8000,
+  });
 
   if (hasAddress) {
     geocodingCep.value = true;
+
     try {
       const geo = await geocodeFullAddress({
-        address:  form.address,
-        number:   form.number,
+        address: form.address,
+        city: form.city?.name,
         district: form.district,
+        number: form.number,
+        state: form.state?.name,
         zip_code: form.zip_code,
-        city:     form.city?.name,
-        state:    form.state?.name,
       });
+
       if (geo) {
         initialLat = geo.lat;
         initialLng = geo.lng;
-        $q.notify({ color: 'indigo-9', textColor: 'white', position: 'top', message: '[ADDR] geocodeFullAddress OK', caption: `lat: ${geo.lat}\nlng: ${geo.lng}`, timeout: 8000, multiLine: true });
+
+        $q.notify({
+          caption: `lat: ${geo.lat}\nlng: ${geo.lng}`,
+          color: "indigo-9",
+          message: "[ADDR] geocodeFullAddress OK",
+          multiLine: true,
+          position: "top",
+          textColor: "white",
+          timeout: 8000,
+        });
       } else {
-        $q.notify({ color: 'indigo-9', textColor: 'white', position: 'top', message: '[ADDR] geocodeFullAddress retornou null — usando coord default', timeout: 6000 });
+        $q.notify({
+          color: "indigo-9",
+          message:
+            "[ADDR] geocodeFullAddress retornou null — usando coord default",
+          position: "top",
+          textColor: "white",
+          timeout: 6000,
+        });
       }
     } catch (err) {
-      $q.notify({ color: 'indigo-9', textColor: 'white', position: 'top', message: '[ADDR] geocodeFullAddress ERRO — usando coord default', caption: `erro: ${err?.message ?? String(err)}`, timeout: 8000, multiLine: true });
+      $q.notify({
+        caption: `erro: ${err?.message ?? String(err)}`,
+        color: "indigo-9",
+        message: "[ADDR] geocodeFullAddress ERRO — usando coord default",
+        multiLine: true,
+        position: "top",
+        textColor: "white",
+        timeout: 8000,
+      });
     } finally {
       geocodingCep.value = false;
     }
   } else {
-    $q.notify({ color: 'indigo-9', textColor: 'white', position: 'top', message: '[ADDR] sem endereço preenchido — abrindo mapa com coord default', timeout: 6000 });
+    $q.notify({
+      color: "indigo-9",
+      message:
+        "[ADDR] sem endereço preenchido — abrindo mapa com coord default",
+      position: "top",
+      textColor: "white",
+      timeout: 6000,
+    });
   }
 
-  const dialogProps = initialLat !== null
-    ? { initialLat, initialLng }
-    : {};
+  const dialogProps =
+    initialLat !== null
+      ? {
+          initialLat,
+          initialLng,
+        }
+      : {};
+
+  $q.notify({
+    caption: `initialLat: ${
+      dialogProps.initialLat ?? "default"
+    }\ninitialLng: ${dialogProps.initialLng ?? "default"}`,
+    color: "indigo-9",
+    message: "[ADDR] abrindo LocationMapDialog",
+    multiLine: true,
+    position: "top",
+    textColor: "white",
+    timeout: 8000,
+  });
 
-  $q.notify({ color: 'indigo-9', textColor: 'white', position: 'top', message: '[ADDR] abrindo LocationMapDialog', caption: `initialLat: ${dialogProps.initialLat ?? 'default'}\ninitialLng: ${dialogProps.initialLng ?? 'default'}`, timeout: 8000, multiLine: true });
+  showMapDialog(dialogProps);
+};
 
+const showMapDialog = (dialogProps = {}) => {
   $q.dialog({
     component: LocationMapDialog,
     componentProps: dialogProps,
   }).onOk(async (geoData) => {
+    form.address = geoData.address || form.address;
+    form.district = geoData.district || form.district;
     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
+    if (!geoData.zip_code) {
+      return;
+    }
+
+    form.zip_code = geoData.zip_code.replace(/\D/g, "");
+
+    try {
+      const addressData = await searchAddressByCEP(form.zip_code);
+
+      if (addressData) {
+        form.city = addressData.city;
+        form.city_id = addressData.city_id;
+        form.state = addressData.state;
+        form.state_id = addressData.state_id;
       }
+    } catch {
+      // Mantém cidade e estado atuais caso a busca falhe.
     }
   });
 };
 
+const useLocation = async () => {
+  loadingLocation.value = true;
+
+  try {
+    const granted = await requestPermission();
+
+    if (!granted) {
+      $q.notify({
+        message: t("auth.location_permission_denied"),
+        type: "warning",
+      });
+
+      return;
+    }
+
+    const position = await getCurrentPosition();
+
+    showMapDialog({
+      initialLat: position.lat,
+      initialLng: position.lng,
+    });
+  } catch (err) {
+    const isPermissionError =
+      err?.code === 1 ||
+      err?.message?.toLowerCase().includes("denied");
+
+    $q.notify({
+      message: isPermissionError
+        ? t("auth.location_permission_denied")
+        : t("auth.location_error"),
+      type: "warning",
+    });
+  } finally {
+    loadingLocation.value = false;
+  }
+};
+
+//
+
 const save = async () => {
   saving.value = true;
+
   try {
     let response;
+
     if (props.isEditing && addressId.value) {
-      response = await updateAddress(getUpdatedFields.value, addressId.value);
+      response = await updateAddress(
+        getUpdatedFields.value,
+        addressId.value,
+      );
     } else {
       response = await createAddress({ ...form });
     }
@@ -305,39 +568,43 @@ const save = async () => {
       onDialogOK(response);
     }
   } catch (error) {
-    console.error('Erro ao salvar endereço:', error);
-    $q.notify({ type: 'negative', message: t('profile.address.error_saving') });
+    console.error("Erro ao salvar endereço:", error);
+
+    $q.notify({
+      message: t("profile.address.error_saving"),
+      type: "negative",
+    });
   } finally {
     saving.value = false;
   }
 };
 
 onMounted(() => {
-  if (props.isEditing && props.addressData) {
-    addressId.value = props.addressData.id;
-
-    const initialData = {
-      zip_code: props.addressData.zip_code || '',
-      address: props.addressData.address || '',
-      number: props.addressData.number || '',
-      complement: props.addressData.complement || '',
-      district: props.addressData.district || '',
-      city_id: props.addressData.city_id || null,
-      state_id: props.addressData.state_id || null,
-      city: props.addressData.city || null,
-      state: props.addressData.state || null,
-      source: 'client',
-      source_id: clientId,
-      address_type: props.addressData.address_type || 'home',
-      latitude: props.addressData.latitude ?? null,
-      longitude: props.addressData.longitude ?? null,
-    };
-
-    Object.assign(form, initialData);
-    setUpdateFormAsOriginal();
+  if (!props.isEditing || !props.addressData) {
+    return;
   }
-});
-</script>
 
-<style scoped>
-</style>
+  addressId.value = props.addressData.id;
+
+  const initialData = {
+    address: props.addressData.address || "",
+    address_type: props.addressData.address_type || "home",
+    city: props.addressData.city || null,
+    city_id: props.addressData.city_id || null,
+    complement: props.addressData.complement || "",
+    district: props.addressData.district || "",
+    latitude: props.addressData.latitude ?? null,
+    longitude: props.addressData.longitude ?? null,
+    number: props.addressData.number || "",
+    source: "client",
+    source_id: clientId,
+    state: props.addressData.state || null,
+    state_id: props.addressData.state_id || null,
+    zip_code: props.addressData.zip_code || "",
+  };
+
+  Object.assign(form, initialData);
+
+  setUpdateFormAsOriginal();
+});
+</script>

+ 2 - 0
src/pages/LoginPage.vue

@@ -156,6 +156,8 @@ const stepThreeForm = ref({
   district: "",
   document: "",
   instructions: "",
+  latitude: null,
+  longitude: null,
   name: "",
   nickname: "",
   no_complement: false,