Parcourir la source

feat: :sparkles: feat (mapa) novo campo no fluxo sem cadastro

no app cliente, quando selecionado fluxo de cadastrar mais tarde, foi corrigida localizacao pelo cep que nao funcionava corretamente, e adiconado novo campo na ultima etapa do formulario para o caso de ainda nao ter preenchido o nome.

fase:prod | origin:melhoria
Gustavo Zanatta il y a 6 heures
Parent
commit
2715a4fce2

+ 14 - 0
src/components/login/LoginStep4Panel.vue

@@ -57,6 +57,8 @@
 import { ref } from "vue";
 import { ref } from "vue";
 import { useQuasar } from "quasar";
 import { useQuasar } from "quasar";
 import { useI18n } from "vue-i18n";
 import { useI18n } from "vue-i18n";
+import { Capacitor } from "@capacitor/core";
+import { Keyboard } from "@capacitor/keyboard";
 
 
 import { useGeocodingApi } from "src/composables/useGeocodingApi";
 import { useGeocodingApi } from "src/composables/useGeocodingApi";
 import { useGeolocation } from "src/composables/useGeolocation";
 import { useGeolocation } from "src/composables/useGeolocation";
@@ -72,6 +74,16 @@ const cep = ref("");
 const loadingCep = ref(false);
 const loadingCep = ref(false);
 const loadingLocation = ref(false);
 const loadingLocation = ref(false);
 
 
+const closeKeyboard = async () => {
+  if (!Capacitor.isNativePlatform()) return;
+
+  try {
+    await Keyboard.hide();
+  } catch {
+    // 
+  }
+};
+
 const onCepChange = async (val) => {
 const onCepChange = async (val) => {
   const cleaned = val?.replace(/\D/g, "") ?? "";
   const cleaned = val?.replace(/\D/g, "") ?? "";
 
 
@@ -82,6 +94,8 @@ const onCepChange = async (val) => {
   loadingCep.value = true;
   loadingCep.value = true;
 
 
   try {
   try {
+    await closeKeyboard();
+
     const result = await geocodeCep(cleaned);
     const result = await geocodeCep(cleaned);
 
 
     if (!result) {
     if (!result) {

+ 2 - 60
src/components/profile/ProfileAddressFormDialog.vue

@@ -385,16 +385,6 @@ const openMapDialog = async () => {
 
 
   const hasAddress = form.address || form.zip_code;
   const hasAddress = form.address || form.zip_code;
 
 
-  $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) {
   if (hasAddress) {
     geocodingCep.value = true;
     geocodingCep.value = true;
 
 
@@ -411,48 +401,12 @@ const openMapDialog = async () => {
       if (geo) {
       if (geo) {
         initialLat = geo.lat;
         initialLat = geo.lat;
         initialLng = geo.lng;
         initialLng = geo.lng;
-
-        $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",
-          message:
-            "[ADDR] geocodeFullAddress retornou null — usando coord default",
-          position: "top",
-          textColor: "white",
-          timeout: 6000,
-        });
       }
       }
-    } catch (err) {
-      $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,
-      });
+    } catch (error) {
+      console.error("Failed to geocode address", error);
     } finally {
     } finally {
       geocodingCep.value = false;
       geocodingCep.value = false;
     }
     }
-  } else {
-    $q.notify({
-      color: "indigo-9",
-      message:
-        "[ADDR] sem endereço preenchido — abrindo mapa com coord default",
-      position: "top",
-      textColor: "white",
-      timeout: 6000,
-    });
   }
   }
 
 
   const dialogProps =
   const dialogProps =
@@ -463,18 +417,6 @@ const openMapDialog = async () => {
         }
         }
       : {};
       : {};
 
 
-  $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,
-  });
-
   showMapDialog(dialogProps);
   showMapDialog(dialogProps);
 };
 };
 
 

+ 0 - 20
src/composables/useGeocodingApi.js

@@ -1,15 +1,7 @@
 import axios from 'axios';
 import axios from 'axios';
-import { Notify } from 'quasar';
 
 
 const GEOCODING_URL = 'https://maps.googleapis.com/maps/api/geocode/json';
 const GEOCODING_URL = 'https://maps.googleapis.com/maps/api/geocode/json';
 
 
-// ── DEBUG via notificação ─────────────────────────────────────────
-const dbg = (title, data = {}) => {
-  const lines = Object.entries(data).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join('\n');
-  Notify.create({ color: 'teal-9', textColor: 'white', position: 'top', message: `[GEO] ${title}`, caption: lines || undefined, timeout: 8000, multiLine: true });
-};
-// ─────────────────────────────────────────────────────────────────
-
 const parseAddressComponents = (components) => {
 const parseAddressComponents = (components) => {
   const get = (type, nameType = 'long_name') =>
   const get = (type, nameType = 'long_name') =>
     components.find((c) => c.types.includes(type))?.[nameType] ?? '';
     components.find((c) => c.types.includes(type))?.[nameType] ?? '';
@@ -36,11 +28,8 @@ export const useGeocodingApi = () => {
     const apiKey = process.env.GOOGLE_MAPS_API_KEY;
     const apiKey = process.env.GOOGLE_MAPS_API_KEY;
     const usandoGeocoderNativo = !!window.google?.maps?.Geocoder;
     const usandoGeocoderNativo = !!window.google?.maps?.Geocoder;
 
 
-    dbg('geocodeCep', { cep: cleaned, via: usandoGeocoderNativo ? 'Geocoder nativo' : 'HTTP', apiKey: apiKey ? `${apiKey.substring(0, 8)}...` : 'UNDEFINED' });
-
     if (usandoGeocoderNativo) {
     if (usandoGeocoderNativo) {
       const results = await geocodeViaGoogleMaps({ address: `${cleaned}, Brazil` });
       const results = await geocodeViaGoogleMaps({ address: `${cleaned}, Brazil` });
-      dbg('geocodeCep nativo result', { total: results.length, encontrou: results.length > 0 });
       if (!results.length) return null;
       if (!results.length) return null;
       const result = results[0];
       const result = results[0];
       const parsed = parseAddressComponents(result.address_components);
       const parsed = parseAddressComponents(result.address_components);
@@ -54,7 +43,6 @@ export const useGeocodingApi = () => {
     const { data } = await axios.get(GEOCODING_URL, {
     const { data } = await axios.get(GEOCODING_URL, {
       params: { address: `${cleaned}, Brazil`, key: apiKey },
       params: { address: `${cleaned}, Brazil`, key: apiKey },
     });
     });
-    dbg('geocodeCep HTTP result', { status: data.status, total: data.results?.length ?? 0 });
     if (data.status !== 'OK' || !data.results.length) return null;
     if (data.status !== 'OK' || !data.results.length) return null;
     const result = data.results[0];
     const result = data.results[0];
     const { lat, lng } = result.geometry.location;
     const { lat, lng } = result.geometry.location;
@@ -65,11 +53,8 @@ export const useGeocodingApi = () => {
     const apiKey = process.env.GOOGLE_MAPS_API_KEY;
     const apiKey = process.env.GOOGLE_MAPS_API_KEY;
     const usandoGeocoderNativo = !!window.google?.maps?.Geocoder;
     const usandoGeocoderNativo = !!window.google?.maps?.Geocoder;
 
 
-    dbg('reverseGeocode chamado', { lat, lng, via: usandoGeocoderNativo ? 'Geocoder nativo' : 'HTTP', apiKey: apiKey ? `${apiKey.substring(0, 8)}...` : 'UNDEFINED' });
-
     if (usandoGeocoderNativo) {
     if (usandoGeocoderNativo) {
       const results = await geocodeViaGoogleMaps({ location: { lat, lng } });
       const results = await geocodeViaGoogleMaps({ location: { lat, lng } });
-      dbg('reverseGeocode nativo result', { total: results.length });
       if (!results.length) return null;
       if (!results.length) return null;
       return { lat, lng, ...parseAddressComponents(results[0].address_components) };
       return { lat, lng, ...parseAddressComponents(results[0].address_components) };
     }
     }
@@ -77,7 +62,6 @@ export const useGeocodingApi = () => {
     const { data } = await axios.get(GEOCODING_URL, {
     const { data } = await axios.get(GEOCODING_URL, {
       params: { latlng: `${lat},${lng}`, key: apiKey },
       params: { latlng: `${lat},${lng}`, key: apiKey },
     });
     });
-    dbg('reverseGeocode HTTP result', { status: data.status, total: data.results?.length ?? 0, errorMessage: data.error_message ?? null });
     if (data.status !== 'OK' || !data.results.length) return null;
     if (data.status !== 'OK' || !data.results.length) return null;
     const result = data.results[0];
     const result = data.results[0];
     return { lat, lng, ...parseAddressComponents(result.address_components) };
     return { lat, lng, ...parseAddressComponents(result.address_components) };
@@ -89,11 +73,8 @@ export const useGeocodingApi = () => {
     const apiKey = process.env.GOOGLE_MAPS_API_KEY;
     const apiKey = process.env.GOOGLE_MAPS_API_KEY;
     const usandoGeocoderNativo = !!window.google?.maps?.Geocoder;
     const usandoGeocoderNativo = !!window.google?.maps?.Geocoder;
 
 
-    dbg('geocodeFullAddress', { query, via: usandoGeocoderNativo ? 'Geocoder nativo' : 'HTTP', apiKey: apiKey ? `${apiKey.substring(0, 8)}...` : 'UNDEFINED' });
-
     if (usandoGeocoderNativo) {
     if (usandoGeocoderNativo) {
       const results = await geocodeViaGoogleMaps({ address: query });
       const results = await geocodeViaGoogleMaps({ address: query });
-      dbg('geocodeFullAddress nativo result', { total: results.length });
       if (!results.length) return null;
       if (!results.length) return null;
       const result = results[0];
       const result = results[0];
       return {
       return {
@@ -106,7 +87,6 @@ export const useGeocodingApi = () => {
     const { data } = await axios.get(GEOCODING_URL, {
     const { data } = await axios.get(GEOCODING_URL, {
       params: { address: query, key: apiKey },
       params: { address: query, key: apiKey },
     });
     });
-    dbg('geocodeFullAddress HTTP result', { status: data.status, total: data.results?.length ?? 0, errorMessage: data.error_message ?? null });
     if (data.status !== 'OK' || !data.results.length) return null;
     if (data.status !== 'OK' || !data.results.length) return null;
     const result = data.results[0];
     const result = data.results[0];
     const { lat, lng } = result.geometry.location;
     const { lat, lng } = result.geometry.location;

+ 2 - 1
src/i18n/locales/en.json

@@ -177,7 +177,8 @@
     "complement_required": "Please fill in the complement or check 'Address without complement'.",
     "complement_required": "Please fill in the complement or check 'Address without complement'.",
     "register_error": "Error completing registration. Please try again.",
     "register_error": "Error completing registration. Please try again.",
     "geocoding_failed": "Address not identified. Drag the pin to adjust the location.",
     "geocoding_failed": "Address not identified. Drag the pin to adjust the location.",
-    "geocoding_failed_short": "Drag the pin to identify the address"
+    "geocoding_failed_short": "Drag the pin to identify the address",
+    "map_unavailable": "Could not open the map. Please try again."
   },
   },
   "business": {
   "business": {
     "advertise": "Advertise",
     "advertise": "Advertise",

+ 2 - 1
src/i18n/locales/es.json

@@ -177,7 +177,8 @@
     "complement_required": "Complete el complemento o marque 'Dirección sin complemento'.",
     "complement_required": "Complete el complemento o marque 'Dirección sin complemento'.",
     "register_error": "Error al finalizar el registro. Inténtelo de nuevo.",
     "register_error": "Error al finalizar el registro. Inténtelo de nuevo.",
     "geocoding_failed": "Dirección no identificada. Arrastre el pin para ajustar la ubicación.",
     "geocoding_failed": "Dirección no identificada. Arrastre el pin para ajustar la ubicación.",
-    "geocoding_failed_short": "Arrastre el pin para identificar la dirección"
+    "geocoding_failed_short": "Arrastre el pin para identificar la dirección",
+    "map_unavailable": "No se pudo abrir el mapa. Inténtelo de nuevo."
   },
   },
   "business": {
   "business": {
     "advertise": "Anunciar",
     "advertise": "Anunciar",

+ 2 - 1
src/i18n/locales/pt.json

@@ -177,7 +177,8 @@
     "complement_required": "Informe o complemento ou marque 'Endereço sem complemento'.",
     "complement_required": "Informe o complemento ou marque 'Endereço sem complemento'.",
     "register_error": "Erro ao finalizar cadastro. Tente novamente.",
     "register_error": "Erro ao finalizar cadastro. Tente novamente.",
     "geocoding_failed": "Endereço não identificado. Arraste o pin para ajustar a localização.",
     "geocoding_failed": "Endereço não identificado. Arraste o pin para ajustar a localização.",
-    "geocoding_failed_short": "Arraste o pin para identificar o endereço"
+    "geocoding_failed_short": "Arraste o pin para identificar o endereço",
+    "map_unavailable": "Não foi possível abrir o mapa. Tente novamente."
   },
   },
   "business": {
   "business": {
     "advertise": "Anunciar",
     "advertise": "Anunciar",

+ 19 - 1
src/pages/location/AddressCompletionPage.vue

@@ -15,6 +15,23 @@
         ref="addressForm"
         ref="addressForm"
         class="address-completion-content"
         class="address-completion-content"
       >
       >
+        <div class="text-text">
+          <span class="font14 fontbold">{{ $t("auth.full_name") }}</span>
+        </div>
+
+        <q-input
+          v-model="form.name"
+          class="bg-surface q-mt-sm q-mb-md"
+          hide-bottom-space
+          input-class="text-text"
+          lazy-rules
+          no-error-icon
+          outlined
+          rounded
+          :placeholder="$t('auth.full_name')"
+          :rules="[inputRules.required]"
+        />
+
         <div class="text-text">
         <div class="text-text">
           <span class="font14 fontbold">{{ $t("common.terms.document") }}</span>
           <span class="font14 fontbold">{{ $t("common.terms.document") }}</span>
         </div>
         </div>
@@ -257,6 +274,7 @@ const addressForm = ref(null);
 const submitting = ref(false);
 const submitting = ref(false);
 
 
 const form = ref({
 const form = ref({
+  name: flowStore.name || "",
   document: flowStore.document || "",
   document: flowStore.document || "",
   number: "",
   number: "",
   no_complement: false,
   no_complement: false,
@@ -302,7 +320,7 @@ const handleConfirm = async () => {
       email: flowStore.email || undefined,
       email: flowStore.email || undefined,
       phone: form.value.phone,
       phone: form.value.phone,
       document: form.value.document,
       document: form.value.document,
-      name: flowStore.name || undefined,
+      name: form.value.name?.trim() || undefined,
       code: flowStore.code,
       code: flowStore.code,
       zip_code: flowStore.confirmedZipCode || undefined,
       zip_code: flowStore.confirmedZipCode || undefined,
       address: flowStore.confirmedAddress || undefined,
       address: flowStore.confirmedAddress || undefined,

+ 59 - 11
src/pages/location/LocationMapPage.vue

@@ -39,7 +39,7 @@
 </template>
 </template>
 
 
 <script setup>
 <script setup>
-import { ref, onMounted, onUnmounted } from 'vue';
+import { ref, nextTick, onMounted, onUnmounted } from 'vue';
 import { useRouter } from 'vue-router';
 import { useRouter } from 'vue-router';
 import { useQuasar } from 'quasar';
 import { useQuasar } from 'quasar';
 import { useI18n } from 'vue-i18n';
 import { useI18n } from 'vue-i18n';
@@ -63,7 +63,35 @@ const currentGeoData = ref(null);
 let googleMap = null;
 let googleMap = null;
 const markerId = ref(null);
 const markerId = ref(null);
 
 
+const transparentEls = [];
+
+const applyMapTransparency = () => {
+  [document.documentElement, document.body, document.getElementById('q-app')]
+    .filter(Boolean)
+    .forEach((el) => {
+      el.style.setProperty('background', 'transparent', 'important');
+      transparentEls.push(el);
+    });
+};
+
+const removeMapTransparency = () => {
+  transparentEls.forEach((el) => el.style.removeProperty('background'));
+  transparentEls.length = 0;
+};
+
+const waitForStableBounds = async (el) => {
+  let previous = -1;
+
+  for (let i = 0; i < 12; i += 1) {
+    const { height } = el.getBoundingClientRect();
+    if (height > 0 && height === previous) return;
+    previous = height;
+    await new Promise((resolve) => setTimeout(resolve, 50));
+  }
+};
+
 const handleBack = async () => {
 const handleBack = async () => {
+  removeMapTransparency();
   await destroyMap();
   await destroyMap();
   router.back();
   router.back();
 };
 };
@@ -71,6 +99,7 @@ const handleBack = async () => {
 const handleConfirm = async () => {
 const handleConfirm = async () => {
   if (!currentGeoData.value) return;
   if (!currentGeoData.value) return;
   flowStore.setConfirmedLocation(currentGeoData.value);
   flowStore.setConfirmedLocation(currentGeoData.value);
+  removeMapTransparency();
   await destroyMap();
   await destroyMap();
   router.push({ name: 'AddressCompletionPage' });
   router.push({ name: 'AddressCompletionPage' });
 };
 };
@@ -125,15 +154,32 @@ onMounted(async () => {
   currentLat.value = lat;
   currentLat.value = lat;
   currentLng.value = lng;
   currentLng.value = lng;
 
 
-  googleMap = await GoogleMap.create({
-    id: 'location-map',
-    element: mapRef.value,
-    apiKey: process.env.GOOGLE_MAPS_API_KEY,
-    config: {
-      center: { lat, lng },
-      zoom: 17,
-    },
-  });
+  applyMapTransparency();
+  await nextTick();
+  await waitForStableBounds(mapRef.value);
+
+  const apiKey = process.env.GOOGLE_MAPS_API_KEY;
+
+  if (!apiKey) {
+    $q.notify({ type: 'negative', message: t('auth.map_unavailable') });
+    return;
+  }
+
+  try {
+    googleMap = await GoogleMap.create({
+      id: 'location-map',
+      element: mapRef.value,
+      apiKey,
+      config: {
+        center: { lat, lng },
+        zoom: 17,
+      },
+    });
+  } catch (error) {
+    console.error('Failed to create map', error);
+    $q.notify({ type: 'negative', message: t('auth.map_unavailable') });
+    return;
+  }
 
 
   markerId.value = await googleMap.addMarker({
   markerId.value = await googleMap.addMarker({
     coordinate: { lat, lng },
     coordinate: { lat, lng },
@@ -159,6 +205,7 @@ onMounted(async () => {
 });
 });
 
 
 onUnmounted(async () => {
 onUnmounted(async () => {
+  removeMapTransparency();
   await destroyMap();
   await destroyMap();
 });
 });
 </script>
 </script>
@@ -169,12 +216,13 @@ onUnmounted(async () => {
   width: 100vw;
   width: 100vw;
   height: 100dvh;
   height: 100dvh;
   overflow: hidden;
   overflow: hidden;
-  background: #e5e3df;
+  background: transparent;
 }
 }
 
 
 .location-map-container {
 .location-map-container {
   width: 100%;
   width: 100%;
   height: 100%;
   height: 100%;
+  background: transparent;
 }
 }
 
 
 .location-map-back-btn {
 .location-map-back-btn {