unite-cms/unite-cms

View on GitHub
src/Bundle/AdminBundle/Resources/assets/vue/components/Fields/Form/GeoLocation.vue

Summary

Maintainability
Test Coverage
<template>
  <form-row :domID="domID" :field="field" :alerts="violations">
    <multi-field :field="field" :val="val" @addRow="addRow" @removeRow="removeByKey" v-slot:default="multiProps">
      <div class="uk-flex uk-flex-middle">
        <div class="uk-flex-1 geo-input">

          <google-places v-if="provider === 'google'" class="uk-search uk-search-default" v-bind="googleOptions()" @placechanged="setGoogleValue(arguments, multiProps.rowKey)">
            <span class="uk-search-icon-flip" uk-search-icon></span>
            <input class="uk-search-input" type="search" placeholder="" :value="geoValues[multiProps.rowKey || 0]" @keypress="preventEnter" />
          </google-places>

          <places v-else class="uk-input" :options="algoliaOptions()" :required="field.required" :id="domID" :value="geoValues[multiProps.rowKey || 0]" @change="setAlgoliaValue(arguments, multiProps.rowKey)" />
        </div>

        <template v-if="shouldEditStairsNumber(values[multiProps.rowKey || 0])">
          <div class="geo-separator">/</div>
          <div>
            <input class="uk-input uk-form-width-small" :placeholder="$t('field.geoLocation.placeholder.stairs_number')" :value="stairsNumber[multiProps.rowKey || 0]" @input="setStairsNumber(arguments, multiProps.rowKey, false)" />
          </div>
        </template>

        <template v-if="shouldEditDoorNumber(values[multiProps.rowKey || 0])">
          <div class="geo-separator">/</div>
          <div>
            <input class="uk-input uk-form-width-small" :placeholder="$t('field.geoLocation.placeholder.door_number')" :value="doorNumber[multiProps.rowKey || 0]" @input="setDoorNumber(arguments, multiProps.rowKey, true)" />
          </div>
        </template>

        <div class="uk-margin-small-left" v-if="geoValues[multiProps.rowKey || 0]">
          <a class="uk-text-danger" @click.prevent="clear(multiProps.rowKey)"><icon name="x" /></a>
        </div>

      </div>
      <div class="uk-alert uk-alert-warning" v-if="provider === 'google' && google_maps_wrong_type">{{ $t('field.geoLocation.wrong_type') }}</div>
    </multi-field>
  </form-row>
</template>
<script>
  import _abstract from "./_abstract";
  import FormRow from './_formRow';
  import MultiField from './_multiField';
  import GooglePlaces from 'vue-google-places/src/VueGooglePlaces'
  import Places from 'vue-places/src/Places';
  import Icon from "../../Icon";
  import {removeIntroSpecType} from "../../../plugins/unite";

  const GoogleTypeMap = {
      locality: 'CITY',
      sublocality: 'SUBLOCALITY',
      street_address: 'STREET_ADDRESS',
      premise: 'STREET_ADDRESS',
      subpremise: 'STREET_ADDRESS',
  };
  const AlgoliaTypeMap = {
      city: 'CITY',
      address: 'STREET_ADDRESS',
  };

  const getProviderType = function(type, providerMap) {
      return Object.keys(providerMap).find(key => providerMap[key] === type);
  };

  export default {

      // Static query methods for unite system.
      queryData(field, unite, depth) { return `${ field.id } {
        provided_by,
        id,
        type,
        display_name,
        latitude,
        longitude,
        bound_south,
        bound_west,
        bound_north,
        bound_east,
        street_number,
        street_name,
        postal_code,
        locality,
        sub_locality,
        country_code,
        stairs_number,
        door_number
      }`; },
      normalizeQueryData(queryData, field, unite) { return queryData; },
      normalizeMutationData(formData, field, unite) { return removeIntroSpecType(formData); },

      // Vue properties for this component.
      extends: _abstract,
      components: { MultiField, FormRow, Places, GooglePlaces, Icon },
      data() {
          return {
            google_maps_wrong_type: false
          }
      },
      computed: {
          geoValues() {
              return this.values.map(val => val.display_name);
          },
          stairsNumber() {
              return this.values.map(val => val.stairs_number);
          },
          doorNumber() {
              return this.values.map(val => val.door_number);
          },
          provider() {
              if(this.field.config.google) {
                  return 'google';
              }

              return 'algolia';
          }
      },
      methods: {

          // Because of an internal issue with vue-places, this is not possible as computed value.
          algoliaOptions() {
              let config = {
                  language: this.$i18n.locale,
              };

              if(this.field.config.algolia) {
                  config = Object.assign(config, {
                      countries: this.field.config.algolia.countries,
                      type: getProviderType(this.field.config.algolia.type, AlgoliaTypeMap),
                      appId: this.field.config.algolia.appId,
                      apiKey: this.field.config.algolia.apiKey,
                  });
              }

              return config;
          },

          googleOptions() {
              let config = {};

              if(this.field.config.google) {
                  config = Object.assign(config, {
                      apiKey: this.field.config.google.apiKey,
                      country: this.field.config.google.countries ? this.field.config.google.countries.join(':') : null,
                      types: this.field.config.google.type ? getProviderType(this.field.config.google.type, AlgoliaTypeMap) : null,
                  });
              }

              return config;
          },

          addRow() {
              this.val.push({
                  provided_by: null,
                  id: null,
                  display_name: null,
                  latitude: null,
                  longitude: null,
                  bound_south: null,
                  bound_west: null,
                  bound_north: null,
                  bound_east: null,
                  street_number: null,
                  street_name: null,
                  postal_code: null,
                  locality: null,
                  sub_locality: null,
                  country_code: null,
                  stairs_number: null,
                  door_number: null,
              });
          },

          shouldEditStairsNumber(value) {
              return value && value.type === 'STREET_ADDRESS';
          },

          shouldEditDoorNumber(value) {
              return value && value.type === 'STREET_ADDRESS';
          },

          preventEnter(event) {
              if(event.key === 'Enter') {
                event.preventDefault();
              }
          },

          setAlgoliaValue(args, key) {

              if(!args[0].hit) {
                  return;
              }

              let value = this.values[key || 0];
              let type = AlgoliaTypeMap[args[0].type] || null;

              if(type === 'STREET_ADDRESS' && !args[0].hit.is_highway) {
                  type = 'SUBLOCALITY';
              }

              let normalizedAddress = {
                  provided_by: 'Algolia',
                  id: args[0].hit.objectID,
                  type: type,
                  display_name: args[0].value,
                  latitude: args[0].latlng.lat,
                  longitude: args[0].latlng.lng,
                  bound_south: null,
                  bound_west: null,
                  bound_north: null,
                  bound_east: null,
                  street_number: null,
                  street_name: type === 'STREET_ADDRESS' ? args[0].name : null,
                  postal_code: type === 'STREET_ADDRESS' ? args[0].postcode : null,
                  locality: type === 'CITY' ? (args[0].hit.village ? args[0].administrative : args[0].name) : args[0].city,
                  sub_locality: args[0].hit.village ? args[0].hit.village[0] : null,
                  country_code: args[0].countryCode,
                  stairs_number: type === 'STREET_ADDRESS' ? value.stairs_number : null,
                  door_number: type === 'STREET_ADDRESS' ? value.door_number : null,
              };
              this.setValue([normalizedAddress], key);
          },

          setGoogleValue(args, key) {

              this.google_maps_wrong_type = false;

              let value = this.values[key || 0] || {};

              let type = GoogleTypeMap[args[0].place.types[0]] || null;

              let normalizedAddress = {
                  provided_by: 'Google',
                  id: args[0].place_id,
                  type: type,
                  display_name: args[0].formatted_address,
                  latitude: args[0].latitude,
                  longitude: args[0].longitude,
                  bound_south: null,
                  bound_west: null,
                  bound_north: null,
                  bound_east: null,
                  street_number: args[0].street_number,
                  street_name: args[0].route,
                  postal_code: args[0].postal_code,
                  locality: args[0].locality,
                  sub_locality: args[0].place.vicinity || null,
                  country_code: args[0].country_code,
                  stairs_number: type === 'STREET_ADDRESS' ? value.stairs_number : null,
                  door_number: type === 'STREET_ADDRESS' ? value.door_number : null,
              };

              if(!type) {
                this.google_maps_wrong_type = true;
              }

              this.setValue([normalizedAddress], key);
          },

          clear(key) {
              this.setValue([{
                provided_by: null,
                id: null,
                display_name: null,
                latitude: null,
                longitude: null,
                bound_south: null,
                bound_west: null,
                bound_north: null,
                bound_east: null,
                street_number: null,
                street_name: null,
                postal_code: null,
                locality: null,
                sub_locality: null,
                country_code: null,
                stairs_number: null,
                door_number: null,
              }], key);
          },

          setStairsNumber(args, key) {
              let value = Object.assign({}, this.values[key || 0], {
                stairs_number: args[0].target.value,
              });
              this.setValue([value], key);
          },
          setDoorNumber(args, key) {
              let value = Object.assign({}, this.values[key || 0], {
                door_number: args[0].target.value,
              });
              this.setValue([value], key);
          },
      }
  }
</script>
<style scoped lang="scss">
  .geo-input {
    max-width: 500px;

    .uk-search-default {
      width: 100%;
    }
  }

  .geo-separator {
    padding: 0 0.5vw;
    min-width: 5px;
    width: 10px;
  }

</style>