Source

components/shared/ClientZonesSelect.vue

<template lang="pug">
Multiselect( 
    ref="multiselect"
    v-model="selected"
    label="name"
    track-by="id"
    :placeholder="$t(placeholder)"
    open-direction="bottom"
    :options="optionsItems"
    :multiple="false"
    searchable
    :loading="isLoading"
    
    :clear-on-select="false"
    :close-on-select="true"
    :selectLabel="$t('geocoding.routing.client_zone_select.select_hint')"
    :deselectLabel="$t('geocoding.routing.client_zone_select.deselect_hint')"
    :selectedLabel="$t('geocoding.routing.client_zone_select.selected')"
    v-bind="options"
    )
    template(v-slot:singleLabel="slotProps")
        span {{selected.formatted||selected.formattedError||'...'}}
    template(slot="noResult")
      span {{$t('geocoding.routing.client_zone_select.no_results')}}
    template(slot="noOptions")
      span {{$t('geocoding.routing.client_zone_select.no_options')}}
</template>
<script>
import Multiselect from 'vue-multiselect'
import {
  normalizeApiAddressResponse,
  getFormattedAddressAlt,
} from '@/utils/address.js'

/**
 * @description Allow selecting an address from a client zone (Address will be reversed from zone lat/lng)
 * @requires i18n
 * @requires Vuex (zones store)
 * @requires address.js (utils)
 * @requires Multiselect (third-party package)
 * @namespace components
 * @category components
 * @subcategory shared/geocoding
 * @module ClientZonesSelect
 **/
export default {
  components: {
    Multiselect,
  },
  props: {
    placeholder: {
      type: String,
      default: 'Tapez pour rechercher',
    },
    extraOptions: {
      type: Object,
      default: () => ({}),
    },
  },
  data() {
    return {
      selected: null,
      selectedReversed: {},
      isLoading: false,
      options: {
        optionsLimit: 100,
        ...this.extraOptions,
      },
    }
  },
  computed: {
    optionsItems() {
      return this.$store.getters['zones/allClientZones'].map((item) => ({
        id: item.id,
        name: item.name,
        lat:
          !!item.access_lat && !!item.access_lng ? item.access_lat : item.lat,
        lng:
          !!item.access_lat && !!item.access_lng ? item.access_lng : item.lng,
      }))
    },
  },
  watch: {
    selected: {
      async handler() {
        if (this.selected && Object.keys(this.selected).length > 0) {
          this.computeSelectedFormattedFieldIfMissing()
        } else {
          this.$emit('input', {})
        }
      },
      deep: true,
    },
  },
  created() {
    this.$store.dispatch('zones/syncClientZones', {
      force: true,
    })
  },
  methods: {
    /**
     * Will computed address reversing lat/lng
     * @todo If access lat/lng gives nothing, try with zone center lat/lng
     */
    async computeSelectedFormattedFieldIfMissing() {
      if (!this.selected.formatted) {
        this.$emit('input', {
          ...this.selected, //Select asap (UX++)
        })
        let response = (this.selectedReversed =
          await this.$geocoding.reverseGeocoding(this.selected))
        let formatted = (this.selected.formatted = getFormattedAddressAlt(
          normalizeApiAddressResponse(response)
        ))
        this.$set(this.selected, 'formatted', formatted)
        if (!formatted) {
          this.$set(
            this.selected,
            'formattedError',
            this.$t(
              'geocoding.routing.client_zone_select.reverse_address_fail',
              {
                zoneName: this.selected.name,
              }
            )
            //`L'adresse n'a pas pu être récupérée (${this.selected.name})`
          )
        }
        this.$emit('input', {
          ...this.selected,
          formatted,
        })
        this.$refs.multiselect.$forceUpdate()
      } else {
        this.$emit('input', {
          ...this.selected,
        })
      }
    },
  },
}
</script>