Files
consumocuidado/components/GoogleAddress.vue
2025-08-14 15:12:29 +02:00

254 lines
6.0 KiB
Vue

<template>
<div class="wrapper">
<label>{{ label }}</label>
<p v-if="value && !place">{{ value }}</p>
<p v-if="place">{{ place.address }}</p>
<p v-show="error" class="error">{{ error }}</p>
<div class="input-and-btn">
<button class="geo-btn" type="button" @click="getGeoLocation">
<v-progress-circular
v-if="loading"
:size="15"
:width="2"
indeterminate
/>
<v-icon v-else small class="geo-icon"> mdi-crosshairs-gps </v-icon>
</button>
<input
ref="googleMap"
:required="!value"
placeholder=""
type="search"
@focus="clear"
/>
<!-- <button class="add-btn" type="button" @click="logPlace">Añadir</button> -->
</div>
</div>
</template>
<script>
import { Loader } from '@googlemaps/js-api-loader'
export default {
props: {
value: {
type: String,
default: '',
},
geo: {
type: Object,
default: null,
},
label: {
type: String,
default: 'Dirección',
},
},
emits: ['addedData'],
data() {
return {
google: null,
autocomplete: null,
geocoder: null,
place: null,
error: null,
loading: false,
}
},
async mounted() {
const config = useRuntimeConfig()
const apiKey = config.public.googleMapsApiKey
if (!apiKey) {
console.error('Google Maps API key is not defined in the runtime config.')
return
}
const googleMapApi = new Loader({
libraries: ['places'],
apiKey: apiKey,
})
try {
await googleMapApi.load()
await this.initializeMap()
} catch (error) {
console.error('Error loading Google Maps API:', error)
this.error = 'Error al cargar el mapa de Google'
}
},
methods: {
initializeMap() {
const mapContainer = this.$refs.googleMap
if (!mapContainer || !window.google) {
this.error = 'Google Maps no disponible'
return
}
this.autocomplete = new window.google.maps.places.PlaceAutocompleteElement(mapContainer)
window.google.maps.event.addListener(this.autocomplete, 'place_changed', () => this.logPlace())
this.geocoder = new window.google.maps.Geocoder()
if (this.geo) {
this.getGeoData(this.geo)
}
},
async getGeoLocation() {
this.place = null
this.error = null
const geoLocation = () =>
new Promise((resolve, reject) => {
if (!navigator.geolocation) {
reject(new Error('Geolocalización no soportada'))
return
}
navigator.geolocation.getCurrentPosition(
(posData) => {
resolve(posData)
},
(error) => {
reject(error)
}
)
} )
try {
this.loading = true
const position = await geoLocation()
const geo = {
lat: position.coords.latitude,
lng: position.coords.longitude,
}
this.getGeoData(geo)
} catch (error) {
console.error(error)
this.error = 'No se puede obtener la ubicación'
this.loading = false
}
},
getGeoData(coordinates) {
if (!this.geocoder) {
this.error = 'Geocoder no inicializado'
this.loading = false
return
}
this.geocoder.geocode({ location: coordinates }, (result, status) => {
if (status === 'OK') {
const data = result[0]
const address = data.formatted_address
const geo = {
latitude: Number(data.geometry.location.lat().toFixed(4)),
longitude: Number(data.geometry.location.lng().toFixed(4)),
}
const city = data.address_components.find(
(element) => element.types[0] === 'locality'
).long_name || ''
this.place = { address, geo, city }
if (this.$refs.googleMap) {
this.$refs.googleMap.value = this.place.address
}
this.$emit('addedData', this.place)
this.loading = false
} else {
this.error = 'No se puede obtener la ubicación'
this.loading = false
}
})
},
clear() {
this.place = null
this.error = null
this.$refs.googleMap.value = null
this.$emit('addedData', this.place)
},
async logPlace() {
this.place = null
this.error = null
try {
const googlePlace = await this.autocomplete.getPlace()
console.log('Google Place:', googlePlace)
const address = googlePlace.formatted_address
const geo = {
latitude: Number(googlePlace.geometry.location.lat().toFixed(4)),
longitude: Number(googlePlace.geometry.location.lng().toFixed(4)),
}
const city =
googlePlace.address_components.find(
(element) => element.types[0] === 'locality'
)?.long_name || ''
this.place = { address, geo, city }
this.$emit('addedData', this.place)
} catch {
this.place = null
this.error = 'Debe introducir una dirección correcta'
}
},
},
}
</script>
<style lang="scss" scoped>
label {
text-align: left;
color: $color-navy;
font-weight: $bold;
font-size: $xs;
display: block;
}
input {
width: 100%;
background-color: $color-grey-inputs;
border: 1px solid $color-grey-inputs-border;
border-radius: 4px;
padding: 9px 5px;
display: block;
outline: none;
font-size: $s;
}
.input-and-btn {
width: 100%;
display: flex;
justify-content: space-between;
input {
width: 90%;
}
button {
height: auto;
background-color: $color-navy;
color: $color-light;
border: 1px solid $color-navy;
border-radius: 5px;
text-transform: uppercase;
padding: 5px 5px;
font-size: $xxs;
@include mobile {
font-size: $xxs;
}
}
.geo-btn {
width: 10%;
min-width: 30px;
}
// .add-btn {
// width: 20%;
// }
}
.error {
color: $color-error;
}
.geo-icon {
color: $color-light !important;
}
</style>