Source

components/shared/SearchModule/SearchModule.vue

<template>
  <div v-if="shouldRender" class="search_module" :class="mode">
    <!-- Button shown when the menu is collapsed -->
    <em
      v-show="isAdvancedSearchWithLayoutCollapsed"
      class="fas fa-search collapsed_search_button"
      @click="switchToSearch"
    />

    <div
      v-show="!isAdvancedSearchWithLayoutCollapsed"
      class="search_module_inner"
    >
      <!-- When results are displayed, user can come back to search selection -->
      <BackTitleButton
        v-show="showBackToSearch"
        :title="$t('search_module.results_view.switch_back_to_search')"
        style="margin-left: 10px"
        @click="inputClick"
      />

      <SMSelection
        v-show="!showBackToSearch"
        ref="selection"
        :show-selected="willShowSelectedItems"
        :results="isViewResults"
        :visible="isSelectionView || isFormMode"
        :suggestions="showSuggestions"
        :fixed-input="isFormMode"
        :back-arrow="showBackToResultsArrow"
        @onQuery="(query) => (lastSearchedString = query)"
        @inputClick="inputClick()"
      >
        <template #input-left>
          <!-- User can switch back to existing results -->
          <em
            v-show="showBackToResultsArrow"
            v-b-tooltip.hover.bottom
            class="fas fa-arrow-left pt-1"
            :title="$t('search_module.search_view.last_results_button_tooltip')"
            @click="switchToResults"
          />
        </template>
      </SMSelection>

      <div class="actions">
        <!-- CLOSE BUTTON (X) -->
        <em
          v-if="!isFormMode"
          v-show="hasSelection"
          class="fas fa-times p-1"
          aria-hidden="true"
          @click="resetSearch"
        />

        <!-- Preselection/History menu button -->
        <em v-if="false" v-show="isFormMode" class="fas fa-history p-1" />
        <em
          v-if="false"
          v-show="!isFormMode"
          class="fas fa-history p-1"
          @click="setView('history_menu')"
        />

        <!-- Validate search button -->
        <em class="fas fa-search p-1" @click="clickSearch" />
      </div>
      <SearchModuleHistoryMenu v-show="showHistoryMenu" />
    </div>

    <!-- FREE MODE -->
    <!-- Advanced search button -->
    <div
      v-if="!isFormMode && !isViewResults"
      class="search_bar__switch_mode_button_wrapper"
    >
      <b-button
        squared
        block
        class="search_bar__switch_mode_button"
        @click="() => $emit('clickFormModeButton')"
      >
        {{ $t('searchModule.buttons.advanced_search') }}
      </b-button>

      <slot name="search-validate-button-free" :vm="this">
        <button
          v-if="!isViewResults"
          class="btn btn-block btn-primary mt-2 float-right"
          @click="clickSearch"
        >
          <span v-show="!$store.state.search_module.isSearchInProgress">
            {{ $t('common.Valider') }}
          </span>
          <span v-show="$store.state.search_module.isSearchInProgress">
            <b-spinner
              class="loader"
              variant="info"
              style="width: 1rem; height: 1rem; margin: 0 auto; display: block"
            />
          </span>
        </button>
      </slot>
    </div>

    <!-- FORM MODE -->
    <!-- Search form (Sidebar menu) -->
    <SMForm
      v-if="isFormMode"
      v-show="shouldShowSearchForm"
      ref="form"
      :results="isViewResults"
      :search-string="lastSearchedString"
      :highlight-vehicles-tab="highlightVehiclesTab"
      :highlight-drivers-tab="highlightDriversTab"
      :highlight-circuits-tab="highlightCircuitsTab"
      :tabs="computedFormTabs"
      :reset-button="hasSelection"
      @validate="clickSearch"
      @reset="resetSearch"
    >
      <template #search-filters>
        <slot name="search-filters" />
      </template>

      <template #date-picker="slotProps">
        <slot name="date-picker" :form-actions="slotProps.formActions" />
      </template>

      <template #search-validate-button>
        <slot
          name="search-validate-button-form"
          :click-search="() => clickSearch()"
          :is-view-results="isViewResults"
        />
      </template>

      <div v-show="!isViewResults">
        <slot name="form-bottom"> </slot>
      </div>
    </SMForm>

    <!-- Free search button -->
    <div
      v-if="computedToggleFreesearch && isFormMode && !isViewResults"
      v-show="!isAdvancedSearchWithLayoutCollapsed"
      class="search_bar__switch_mode_button_wrapper"
    >
      <b-button
        variant="outline-primary"
        squared
        block
        class="search_bar__switch_mode_button"
        @click="() => $emit('clickFreeModeButton')"
      >
        {{ $t('searchModule.buttons.free_search') }}
      </b-button>
    </div>

    <!-- FREE/FORM MODE -->
    <!-- Results slot -->
    <div v-show="isViewResults" class="results">
      <slot name="results" />
    </div>
  </div>
</template>
<script>
import SMSelection from './SMSelection.vue'
import SearchModuleHistoryMenu from './SMHistoryMenu.vue'
import SMForm from './SMForm.vue'
import Vue from 'vue'
import BackTitleButton from '@c/shared/BackTitleButton.vue'

/**
 * Shared Form/Free search component used by Location, Alerts, Identification, etc
 * @namespace components
 * @category components
 * @subcategory search-module
 * @module SearchModule
 **/
export default {
  components: {
    SMSelection,
    SearchModuleHistoryMenu,
    SMForm,
    BackTitleButton,
  },
  inject: {
    /**
     * Allow to define search form tabs
     */
    searchFormTabs: {
      default: [],
    },
  },
  props: {
    value: {
      type: Object,
      default: () => null,
    },
    /**
     * free: Renders the free mode (Saisie libre)
     * advanced/form: Renders a form (Like GeoredV2)
     * empty string: Renders free mode (only if menu is collapsed) (This was the initial POC for the input center on the top of the map)
     * map: not implemented
     */
    mode: {
      type: String,
      validator: (value) =>
        ['', 'advanced', 'form', 'free', 'map'].includes(value),
      default: '',
    },
    highlightVehiclesTab: {
      type: Boolean,
      default: false,
    },
    highlightDriversTab: {
      type: Boolean,
      default: false,
    },
    highlightCircuitsTab: {
      type: Boolean,
      default: false,
    },
    formTabs: {
      type: Array,
      default: () => [],
    },
    canToggleFreesearch: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      lastSearchedString: '',
    }
  },

  computed: {
    view: {
      get() {
        return this.$store.state.search_module.view
      },
      set(v) {
        this.$store.state.search_module.view = v
      },
    },
    computedToggleFreesearch() {
      return this.searchModuleCanToggleFreesearch !== null
        ? this.searchModuleCanToggleFreesearch
        : this.canToggleFreesearch
    },
    computedFormTabs() {
      return this.formTabs.length !== 0 ? this.formTabs : this.searchFormTabs
    },
    hasResults() {
      return this.$store.getters['search_module/hasResults']
    },
    showBackToSearch() {
      return this.isFormMode && this.isViewResults
    },
    showBackToResultsArrow() {
      return (
        this.isFormMode &&
        !this.isViewResults &&
        this.$store.getters['search_module/hasResults']
      )
    },
    /**
     * Only for location module
     * @todo Improve/Remove/Attention
     */
    isAdvancedSearchWithLayoutCollapsed() {
      if (this.$store.state.app.layoutType === 'tlayout') {
        return (
          this.isFormMode && this.$store.getters['app/layout'].isMenuCollapsed
        )
      } else {
        return false
      }
    },
    shouldShowSearchForm() {
      return !this.isAdvancedSearchWithLayoutCollapsed
    },
    shouldRender() {
      if (this.mode === 'free') {
        return true
      }

      if (this.isFormMode) {
        return true
      } else {
        //@todo: Showing the search input in the middle-screen (on top of the map) will be deprecated in the near-future (maybe)
        return (
          this.$store.getters['app/layout'].isMenuCollapsed &&
          this.$store.getters['app/layout'].willMenuCollapseCompletely
        )
      }
    },
    isViewResults() {
      return this.isFormMode && this.view == 'results'
    },
    isFormMode() {
      return ['form', 'advanced'].includes(this.mode)
    },
    willShowSelectedItems() {
      return !this.isFormMode
    },
    showSuggestions() {
      return !this.isFormMode
    },
    isSelectionView() {
      return this.view === 'selection'
    },
    /**
     * @warn Unused: Favorites/Last search selection/Most used search selection feature
     */
    showHistoryMenu() {
      return this.view === 'history_menu'
    },
    hasSelection() {
      return this.$store.getters['search_module/hasSelection']
    },
  },
  watch: {
    hasResults() {
      if (this.hasResults) {
        this.switchToResults()
      }
    },
  },
  created() {
    this.view = 'selection'
  },
  async mounted() {
    await this.$store.dispatch('search_module/initialize')
  },
  methods: {
    switchToResults(e) {
      this.setView('results')
      this.$store.dispatch('app/changeLayout', {
        menu_full_collapse: false,
      })
      this.$emit('clickFormModeButton')
      e && e.stopPropagation()
    },
    /**
     * Switch back to search when the layout is collapsed
     */
    switchToSearch(e) {
      this.$log.debug('switchToSearch')
      this.$store.dispatch('app/changeLayout', {
        menu: true,
        menu_collapsed: false,
        menu_full_collapse: true,
        sub_menu: false,
      })
      this.setViewSelection()
      e && e.stopPropagation()
    },
    resetSearch() {
      this.clearSelection()
      this.$emit('clear')
      this.switchToSearch()
    },
    inputClick() {
      this.setViewSelection()
      this.$emit('inputClick')
    },
    focusQueryInput() {
      this.$refs.selection.focusInput()
    },
    clearSelection() {
      this.$store.dispatch('search_module/clearSelection')
      this.$store.dispatch('search_module/clearDateSelection')
      this.$refs.selection.hideSuggestions()
    },
    setViewSelection() {
      this.setView('selection')
      this.$refs.selection.showSuggestions()
    },
    setView(view) {
      this.view = view
      this.$emit('viewChange', view)
    },
    clickSearch() {
      if (!this.$store.getters['search_module/isValidSelection']) {
        return
      }

      this.$emit('input', this.$store.getters['search_module/getSelection'])
      this.$store.dispatch('search_module/validateSearch')
    },
  },
}
</script>
<style lang="scss" scoped>
.search_module {
  width: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  flex-direction: column;
}
.search_module.advanced {
  position: initial;
}

.search_module_inner {
  display: grid;
  grid-template-columns: 1fr;
  position: relative;
  background-color: white;
  width: inherit;
  min-height: 44px;
}
.actions {
  font-size: 25px;
  padding: 0.1rem;
  position: absolute;
  width: fit-content;
  //margin-top: 5px;
  top: 0px;
  right: 5px;
}
em {
  color: var(--color-denim);
  cursor: pointer;
  font-size: 16px;
}
em:hover {
  opacity: 0.9;
}
em.fa-times {
  font-size: 24px;
  position: relative;
  top: 2px;
}

.btn {
  background-color: var(--color-search-module-button);
  color: #484848;
}
.btn.btn-primary {
  color: #fff;
  background-color: #007bff;
  border-color: #007bff;
}
.search_bar__switch_mode_button {
  max-width: 570px;
  border: 0px;
  outline: 0px;
  box-shadow: none;
  font: normal normal normal 12px/14px Open Sans;
  letter-spacing: 0px;
}
.search_bar__switch_mode_button_wrapper {
  max-width: 570px;
  width: inherit;
  position: relative;
  z-index: 1000;
}
.collapsed_search_button {
  color: var(--color-dark-blue);
  font-size: 25px;
  margin-top: 20px;
  margin-bottom: 20px;
}
.results {
  width: inherit;
}
</style>