<template>
<div class="locate-address-latlng">
<b-alert
v-model="hasErrors"
class="mt-2 mx-auto"
dismissible
variant="danger"
fade
@dismissed="resetErrors"
>
{{ errorMessage }}
</b-alert>
<form-builder
v-if="state === 'search'"
v-model="formData"
:sections="sections"
has-valid-btn
class="mt-2 form-builder-wrapper"
theme="theme-simpliciti"
@update-column-value="setColumnValue"
@valid="validForm"
@invalid="setInvalidForm"
/>
<LocateAddressResults
v-if="state === 'results'"
:items="results"
:selected-item="selectedItem"
@select="select"
@preview="centerOnMarker"
@goBack="resetForm"
/>
</div>
</template>
<script>
import FormBuilder from '../Builders/FormBuilder'
import L from 'leaflet'
import LocateAddressResults from '@c/shared/geocoding/LocateAddressResults.vue'
import {
normalizeApiAddressResponse,
getFormattedAddressAlt,
} from '@/utils/address.js'
import geocodingMixin from './geocoding-mixin.js'
import { useMapContextMenu } from '@/components/shared/SimplicitiMap/MapContextMenu.vue'
import { watchEffect } from 'vue'
const { menuContextSelectedOption, menuContextLatLng } = useMapContextMenu()
/**
* @namespace components
* @category components
* @subcategory shared/geocoding
* @module LocateAddressLatLng
*/
export default {
name: 'LocateAddressLatLng',
components: {
LocateAddressResults,
FormBuilder,
},
mixins: [geocodingMixin],
data() {
return {
formData: {
latitude: '',
longitude: '',
},
hasErrors: false,
state: 'search',
response: null,
errorMessage: '',
selectedItem: null,
sections: [
{
name: 'coordinates_section',
header: false,
collapsable: false,
rowsClass: 'mb-3',
rows: [
{
id: 'latitude_row',
columns: [
{
label: 'latitude',
type: 'text',
title: 'latitude',
value: '',
validation: {
message: '',
state: null,
required: true,
hint: 'Ce champ est requis',
},
},
],
},
{
id: 'longitude_row',
columns: [
{
label: 'longitude',
type: 'text',
title: 'longitude',
value: '',
validation: {
message: '',
state: null,
required: true,
hint: 'Ce champ est requis',
},
},
],
},
],
},
],
}
},
computed: {
results() {
return this.response.map((item) => {
let formatted = getFormattedAddressAlt(
normalizeApiAddressResponse(this.response[0])
)
return {
value: formatted,
item: {
...item,
formatted,
},
}
})
},
},
watch: {
async selectedItem() {
this.drawMarkerAndZoom()
},
},
async mounted() {
this.bindFormAutofillOnMapClick()
watchEffect(
this.reactToMapMenuContextNearbyVehiclesOptionAndPeformOperation
)
},
destroyed() {
this.unbindFormAutofillOnMapClick()
},
methods: {
/**
* If the user clicks the nearby vehicle options in the map menu context, the reverse operation will be performed automatically.
*/
reactToMapMenuContextNearbyVehiclesOptionAndPeformOperation() {
if (
menuContextSelectedOption.value === 'nearbyVehicles' &&
this.state === 'search'
) {
this.validateFormUsingMapMenuContextLatLng()
}
},
async drawMarkerAndZoom() {
if (this.selectedItem) {
await this.addLocateAddressMarker(
this.selectedItem.lat || this.selectedItem.latitude,
this.selectedItem.lng || this.selectedItem.longitude,
this.selectedItem.formatted
)
this.centerOnMarker()
}
},
unbindFormAutofillOnMapClick() {
try {
this.simplicitiMap &&
this.simplicitiMap
.getLeafletMapWrapper()
.map.off('click', this.mapClickHandler)
} catch (err) {
console.warn('Fail to unbind form auto fill on map click', err)
}
},
bindFormAutofillOnMapClick() {
let simplicitiMap = (this.simplicitiMap = this.$map.getSimplicitiMapVM())
simplicitiMap &&
simplicitiMap.waitForMapRef(() => {
simplicitiMap.getLeafletMapWrapper().map.on(
'click',
(this.mapClickHandler = async (e) => {
//Skip if form is not visible in the screen
if (this.$el.style.display === 'none') {
return
}
this.formData.latitude = e.latlng.lat
this.formData.longitude = e.latlng.lng
})
)
})
},
validateFormUsingMapMenuContextLatLng() {
if (
menuContextLatLng.value.lat !== null &&
menuContextLatLng.value.lng !== null
) {
this.formData.latitude = menuContextLatLng.value.lat
this.formData.longitude = menuContextLatLng.value.lng
this.validForm()
}
},
async validForm() {
this.hasErrors = false
this.errorMessage = ''
const coords = {
latitude: parseFloat(this.formData.latitude),
longitude: parseFloat(this.formData.longitude),
}
await this.$geocoding.reverseGeocoding(coords).then(async (response) => {
if (response.latitude && response.longitude) {
this.response = [response]
this.selectedItem = this.results[0].item
this.state = 'results'
} else {
this.hasErrors = true
this.errorMessage = this.$t('geocoding.not_address_found')
}
})
},
select(event) {
this.selectedItem = event
this.centerOnMarker(this.selectedItem)
},
resetForm() {
this.state = 'search'
this.response = null
this.$map.getLeafletWrapperVM().layerGroups.locateAddressMarker.remove()
},
centerOnMarker() {
if (this.selectedItem) {
this.$map
.getLeafletInstance()
.flyTo([this.selectedItem.latitude, this.selectedItem.longitude], 14)
}
},
setColumnValue(options) {
let column = null
this.sections.find((section) => {
!!section.rows.find((row) => {
column = row.columns.find((column) => column.label === options.label)
return !!column
})
})
if (column && event.type !== 'blur') {
column.value = options.event
}
if (column.validation) {
if (!column.value && column.validation.required) {
column.validation.state = false
column.validation.message = column.validation.hint
} else {
column.validation.state = true
}
}
},
setInvalidForm(event) {
let column
this.sections.find((section) => {
section.rows.find((row) => {
column = row.columns.find((column) => column.label === event.label)
if (column !== undefined && column.validation) {
column.validation.state = false
column.validation.message = column.validation.hint
}
})
})
},
resetErrors() {
this.errorMessage = ''
this.hasErrors = false
},
},
}
</script>
<style lang="scss" scoped>
.form-builder-wrapper {
font-size: 12px;
}
.map-popup-wrapper {
border-radius: 5px;
border: 1px var(--color-denim) solid;
}
</style>
Source