Source

views/EventsModule.vue

<template lang="pug">
TLayout(
  :syncWithVuex="false"
  :sidebar="true" 
  :menu="true" 
  :menuCollapsed="menuCollapsed" 
  :menuFullCollapse="true" 
  :menuToggle="true"
  :rightMenu="true"
  :rightMenuBottom="mode==='table_map'"
  )
  template(v-slot:sidebar)
    Sidebar
  template(v-slot:menu)
      SearchWrapper(
          @search-input="onSearchModuleSelectionChange"
          @search-view-change="onSearchModuleViewChange"
          @search-clear="onSearchModuleClearSelection"
      )
          template(v-slot:search-module-filters)
              //Main search filters (event types categories and custom information)
              .row.p-0.m-0
               
                .col-12.m-0.mt-2.p-0(v-show="operationAnomaliesTypes.length>0")
                  .filter_item
                      label.filter-label 
                        span {{$t('events.search_filters.anomaly_title')}}
                        em.fas.fa-info(:title="$t('events.search_filters.anomaly_title')+' ('+$t('events.filters_tootip')+')'" v-b-tooltip.hover.right style="padding-left: 5px;")
                      b-form-select(v-model="selectedAnomaliesTypes" multiple :options="operationAnomaliesTypes" size="sm"
                      style="resize: vertical;"
                      ) 
                .col-12.m-0.mt-2.p-0(v-show="operationMessageTypes.length>0")
                  .filter_item
                      label.filter-label 
                        span {{$t('events.search_filters.operation_message_title')}}
                        em.fas.fa-info(:title="$t('events.search_filters.operation_message_title')+' ('+$t('events.filters_tootip')+')'" v-b-tooltip.hover.right style="padding-left: 5px;")
                      b-form-select(v-model="selectedOperationMessageTypes" multiple :options="operationMessageTypes" size="sm"
                      style="resize: vertical;"
                      ) 
                .col-12.m-0.mt-2.p-0(v-show="operationStatusTypes.length>0")
                  .filter_item
                      label.filter-label 
                        span {{$t('events.search_filters.status_title')}}
                        em.fas.fa-info(:title="$t('events.search_filters.status_title')+' ('+$t('events.filters_tootip')+')'" v-b-tooltip.hover.right style="padding-left: 5px;")
                      b-form-select(v-model="selectedStatusTypes" multiple :options="operationStatusTypesComputed" size="sm"
                      style="resize: vertical;"
                      ) 
                //.col-12.m-0.mt-2.p-0
                  .filter_item
                      label.filter-label 
                        span {{$t('events.search_filters.extra_information_title')}}
                        //em.fas.fa-info(:title="$t('events.search_filters.extra_information_title')" v-b-tooltip.hover.right style="padding-left: 5px;")
                      .radio
                        input(type="checkbox" name="extraInfo" v-model="isAggregateEventGroupBinEnabled" @click="e=>aggregateEventGroupSelect(e,'bin')" )
                        label.pl-1 {{$t('events.search_filters.bin_group_label')}}
                      .radio
                        input(type="checkbox" name="extraInfo" v-model="isAggregateEventGroupRoundEnabled" @click="e=>aggregateEventGroupSelect(e,'round')")
                        label.pl-1 {{$t('events.search_filters.round_group_label')}}
                
          template(v-slot:search-module-results)
            .row.m-0.p-0(v-show="listItems.length===0&&!$store.state.search_module.isSearchInProgress")
              .col-12
                span {{$t('events.no_results')}}
            EventsListToolbar.mb-2(v-show="listItems.length>0" 
                @map="()=>onToolbarAction({action: 'map',type: 'total'})" 
                @table="()=>onToolbarAction({action: 'table',type: 'total'})" 
                @markers="()=>onToolbarAction({action: 'markers',type: 'total'})" 
              )
            EventsList(v-show="listItems.length>0" :items="listItems"
              :highlightedSelectionId="highlightedSelectionId"
              @toolbar="onToolbarAction"
              ref="il1"
            )
  template(v-slot:right_menu)
      div.table-layout(v-if="mode==='table'")
        EventsTable(:items="detailedItems"
          :mode="mode"
          @mode="switchMode"
          :headerText="tableHeaderText"
          :showRoundColumns="isAggregateEventGroupRoundEnabled"
        :showBinColumns="isAggregateEventGroupBinEnabled"
          )
      LocationEventsMap(v-if="mode==='table_map'||mode==='list'" ref="map")
  template(v-slot:right_menu_bottom)
      EventsTable(v-if="mode==='table_map'" 
        :mode="mode"
        :items="detailedItems"
        :headerText="tableHeaderText"
        @mode="switchMode"
        :showRoundColumns="isAggregateEventGroupRoundEnabled"
        :showBinColumns="isAggregateEventGroupBinEnabled"
        )
</template>
<script>
import Vue from 'vue'
import EventsTable from '@/components/events/EventsTable.vue'
import LocationEventsMap from '@/components/location/LocationEvents/LocationEventsMap.vue'
import EventsList from '@/components/events/EventsList.vue'
import EventsListToolbar from '@/components/events/EventsListToolbar.vue'
import Sidebar from '@c/shared/Sidebar/Sidebar.vue'
import TLayout from '@c/shared/TLayout/TLayout.vue'
import SearchWrapper from '@c/shared/SearchModule/SearchWrapper/SearchWrapper.vue'
import { mapGetters } from 'vuex'
import { getEventsSearchSelectionLimit } from '@/services/search-service.js'
import { getQueryStringValue } from '@/utils/querystring'
import moment from 'moment'
import i18n from '@/i18n'
import {
  fetchEvents,
  fetchEventsCumulation,
  getEventTypesCategories,
} from '@/services/events-service.js'
import { getEnvValue } from '@/services/env-service.js'
import { normalizeString } from '@/utils/string.js'

const canGroupEventsByAll = getEnvValue('events_enable_group_all', '1') === '1' //Allow user to select bin and round (group=all) (Enabled by default)

export const EventsModuleIsolatedConfig = [
  'EventsModule',
  () =>
    /* webpackChunkName: "testing"    */
    import('@/views/EventsModule.vue'),
  {
    props: {
      //Custom props
    },
    beforeEnter: (to, from, next) => {
      //Custom logic before routing in
      return !!next && next()
    },
  },
]

function eventsFilterMixin() {
  return {
    data() {
      return {
        //Filters data
        operationMessageTypes: [],
        operationAnomaliesTypes: [],
        operationStatusTypes: [],

        //Custom search filters
        selectedOperationMessageTypes: [],
        selectedAnomaliesTypes: [],
        selectedStatusTypes: [],
      }
    },
    computed: {
      isAggregateEventGroupBinEnabled() {
        return this.$store.getters['settings/getParameter'](
          'isAggregateEventGroupBinEnabled'
        )
      },
      isAggregateEventGroupRoundEnabled() {
        return this.$store.getters['settings/getParameter'](
          'isAggregateEventGroupRoundEnabled'
        )
      },
      selectedAggregateEventGroups() {
        if (
          this.isAggregateEventGroupBinEnabled &&
          this.isAggregateEventGroupBinEnabled &&
          canGroupEventsByAll
        ) {
          return 'all'
        }
        if (this.isAggregateEventGroupBinEnabled) return 'bin'
        if (this.isAggregateEventGroupRoundEnabled) return 'round'
        return ''
      },
      /**
       * Translate label on the fly
       */
      operationStatusTypesComputed() {
        return this.operationStatusTypes.map((item) => {
          return {
            value: item.value,
            text: getI18nLabelForEventTypeCategoryLabel(
              item.text,
              'event_types.status.'
            ),
          }
        })
      },
    },
    methods: {
      /*       aggregateEventGroupSelect(e, type) {
        if (e.target.value && type === 'round' && !canGroupEventsByAll) {
          this.isAggregateEventGroupBinEnabled = false
        }
        if (e.target.value && type === 'bin' && !canGroupEventsByAll) {
          this.isAggregateEventGroupRoundEnabled = false
        }
      }, */
    },
    created() {
      getEventTypesCategories((dataSubset) => {
        const transformEventTypeCategoryItem = (item) => ({
          value: item.code,
          text: item.label,
        })
        dataSubset.forEach((item) => {
          switch (item.type) {
            case 'operation_message':
              this.operationMessageTypes.push(
                transformEventTypeCategoryItem(item)
              )
              break
            case 'anomaly':
              this.operationAnomaliesTypes.push(
                transformEventTypeCategoryItem(item)
              )
              break
            case 'status':
              this.operationStatusTypes.push(
                transformEventTypeCategoryItem(item)
              )
              break
            default:
              break
          }
        })

        /* 
        //Generate a JSON with missing keys
        let json = {}
        this.operationAnomaliesTypes.forEach((item) => {
          let i18nCode = getI18nLabelForEventTypeCategoryLabel(item.text, 'event_types.anomaly.',true)
          let exists = i18n.te(i18nCode)
          if (!exists) {
            json[i18nCode] = ''
          }
        })
        console.warn('Missing event types operationAnomaliesTypes i18n codes')
        console.log(JSON.stringify(json, null, 4))*/
      })
    },
  }
}

function getI18nLabelForEventTypeCategoryLabel(
  text,
  prefix,
  returnCodeInstead = false
) {
  const input = text
  const options = {
    replaceSpaces: true,
    convertToLowercase: true,
    removeParentheses: true,
    removeAccents: true,
    prefix,
  }

  const i18nCode = normalizeString(input, options)
  if (returnCodeInstead) {
    return i18nCode
  }
  const exists = i18n.te(i18nCode)
  if (exists) {
    //console.warn('i18n code found', i18nCode, text, i18n.t(i18nCode))
    return i18n.t(i18nCode)
  } else {
    //console.warn('i18n code not found', i18nCode, text)
    return text
  }
}

/**
 * Computes "tableHeaderText"
 */
function tableHeaderMixin() {
  return {
    data() {
      return {
        //Necessary to compute table header text
        dateFromFormatted: '',
        dateToFormatted: '',
        //detailedItemsBy: external
        //detailedItemsByItem: external
      }
    },
    computed: {
      tableHeaderText() {
        if (this.detailedItemsBy === 'total') {
          return this.$t('events.table.header_text.total', {
            fromDate: this.dateFromFormatted,
            toDate: this.dateToFormatted,
          })
        }
        if (this.detailedItemsBy === 'date') {
          return this.$t('events.table.header_text.date', {
            date: this.detailedItemsByItem.dateItem.label,
          })
        }
        if (this.detailedItemsBy === 'date-aggregate') {
          return this.$t('events.table.header_text.date_aggregate', {
            date: this.detailedItemsByItem.dateItem.label,
            type: this.detailedItemsByItem.eventGroup.type,
            code: this.detailedItemsByItem.eventGroup.code,
          })
        }
        if (
          this.detailedItemsBy === 'subitem' &&
          this.activeSearchFormTabName === 'vehicle'
        ) {
          return this.$t('events.table.header_text.vehicle', {
            vehicleName: this.detailedItemsByItem.subItem.label,
            date: this.detailedItemsByItem.dateItem.label,
          })
        }
        if (
          this.detailedItemsBy === 'subitem' &&
          this.activeSearchFormTabName === 'circuit'
        ) {
          return this.$t('events.table.header_text.circuit', {
            circuitName: this.detailedItemsByItem.subItem.label,
            date: this.detailedItemsByItem.dateItem.label,
          })
        }

        if (
          this.detailedItemsBy === 'aggregate' &&
          this.activeSearchFormTabName === 'vehicle'
        ) {
          return this.$t('events.table.header_text.vehicle_aggregate', {
            vehicleName: this.detailedItemsByItem.subItem.label,
            date: this.detailedItemsByItem.dateItem.label,
            type: this.detailedItemsByItem.eventGroup.type,
            code: this.detailedItemsByItem.eventGroup.code,
          })
        }
        if (
          this.detailedItemsBy === 'aggregate' &&
          this.activeSearchFormTabName === 'circuit'
        ) {
          return this.$t('events.table.header_text.circuit_aggregate', {
            circuitName: this.detailedItemsByItem.subItem.label,
            date: this.detailedItemsByItem.dateItem.label,
            type: this.detailedItemsByItem.eventGroup.type,
            code: this.detailedItemsByItem.eventGroup.code,
          })
        }

        return ''
      },
    },
    methods: {
      /**
       * Uses this.items (external)
       */
      getVehicleName(id) {
        return (
          ((this.items || []).find((i) => i.vehicleId == id) || {})
            .vehicleName || ''
        )
      },
      getCircuitName(vehicleId, circuitId) {
        let synthesisVehicleItem =
          (this.items || []).find((i) => i.vehicleId == vehicleId) || {}
        let circuitSynthesis =
          (
            synthesisVehicleItem.circuits ||
            synthesisVehicleItem.synthesis ||
            []
          ).find((s) => s.circuitId == circuitId) || {}
        return circuitSynthesis.circuitName || ''
      },
    },
  }
}

export default {
  name: 'EventsModule',
  componentType: 'container',
  components: {
    SearchWrapper,
    TLayout,
    EventsTable,
    LocationEventsMap,
    EventsList,
    EventsListToolbar,
    Sidebar,
  },
  mixins: [
    eventsFilterMixin(),
    tableHeaderMixin(),
    Vue.$mixins.userRightsMixin,
  ],
  provide() {
    let self = this
    let searchFormTabs = ['disabled']

    if (
      this.hasFeatureRight('events_search_vehicle') ||
      (getQueryStringValue('test') === '1' && !this.$env.isProduction())
    ) {
      searchFormTabs.push({
        label: i18n.t(`common.Véhicule`),
        value: 'vehicle',
      })
    }

    if (
      this.hasFeatureRight('events_search_circuit') ||
      (getQueryStringValue('test') === '1' && !this.$env.isProduction())
    ) {
      searchFormTabs.push({
        label: i18n.t(`common.Circuit`),
        value: 'circuit',
      })
    }

    if (searchFormTabs.length > 1) {
      searchFormTabs.splice(0, 1)
    }

    return {
      showCustomFiltersByDefault: false,
      /**
       * Provide handler to react to date picker predefined options change and limit date range selection if necessary
       * @function onSearchModuleDatePickerPredefinedSelectionClick
       * @see SMDatePicker.vue Date picker will inject this handler
       */
      onSearchModuleDatePickerPredefinedSelectionClick({
        length,
        limitDateSelectionRangeToNDays,
      }) {
        let selectedElementsIds = self.$store.getters[
          'search_module/getSelectedIdsByType'
        ](self.activeSearchFormTabName)
        let willCurrentDateSelectionExceedSelectionLimit =
          selectedElementsIds.length * length > getEventsSearchSelectionLimit()
        if (willCurrentDateSelectionExceedSelectionLimit) {
          window.alert(
            `La sélection pour la recherche a été réduite à ${getEventsSearchSelectionLimit()} éléments`
          )
          let maxDateRangeLength = Math.round(
            (getEventsSearchSelectionLimit() - 1) / selectedElementsIds.length -
              1
          )
          limitDateSelectionRangeToNDays(maxDateRangeLength)
        }
      },
      searchModuleCanToggleFreesearch: false,
      searchFormTabs,
    }
  },
  data() {
    return {
      mode: 'list',
      menuCollapsed: false,
      //search selection
      elementIdsUsedDuringLastSearch: [],
      datesUsedDuringLastSearch: [],
      //Synthesis dataset (see: event-service.js:fetchEventsCumulation)
      items: [],
      isFetchDetailsInProgress: false,
      //detailedItems (computed) (use can choose to view table data at different levels)
      detailedItemsBy: 'total', //total/date/date-aggregate/subitem/aggregate
      detailedItemsByItem: null, //metadata {dateItem:{},subItem:{},eventGroup:{}}
      detailedItems: [],
    }
  },
  computed: {
    ...mapGetters({
      activeSearchFormTabName: 'search_module/activeSearchFormTabName',
      selection: 'search_module/getSelection',
    }),

    listItems() {
      return [...this.items].sort((a, b) =>
        a.timestamp < b.timestamp ? 1 : -1
      )
    },

    /**
     * We highlight the current selection (date/date-aggregate/subitem/aggregate)
     * @todo Support date/date-aggregate type
     */
    highlightedSelectionId() {
      let dateId = this.detailedItemsByItem?.dateItem?.id || 'x'
      let subItemId = this.detailedItemsByItem?.subItem?.id || 'x' //vehicle/circuit
      let aggregateId = this.detailedItemsByItem?.eventGroup?.id || 'x'
      return `${this.detailedItemsBy}_${dateId}_${subItemId}_${aggregateId}`
    },
  },
  watch: {
    isAggregateEventGroupBinEnabled() {
      this.forceTableReload()
    },
    isAggregateEventGroupRoundEnabled() {
      this.forceTableReload()
    },
    /*  aggregateSettingsUpdated() {
      let currentmode = this.mode
      console.log('aggregateSettingsUpdated', currentmode)
      if (this.mode != 'list') {
        this.switchMode('list')
        this.switchMode(currentmode)
      }
    }, */
    /**
     * If table-map/list mode, show markers in the map as soon as data is ready.
     */
    detailedItems() {
      if (this._prepareMarkersTimeout) {
        clearTimeout(this._prepareMarkersTimeout)
      }
      this._prepareMarkersTimeout = setTimeout(() => {
        this.prepareMapMarkers()
        clearTimeout(this._prepareMarkersTimeout)
      }, 500)
    },
    /**
     * Toggle sidebar result view on/off (list)
     */
    items() {
      if (this.items.length === 0) {
        this.$store.dispatch('search_module/setHasResults', false)
      } else {
        if (this.$store.getters['search_module/hasResults']) {
          this.$store.dispatch('search_module/setHasResults', false)
          this.$nextTick(() =>
            this.$store.dispatch('search_module/setHasResults', true)
          )
        } else {
          this.$store.dispatch('search_module/setHasResults', true)
        }
      }
    },
  },
  mounted() {
    //By default, map is empty
    this.clearMapMarkers()
  },
  destroyed() {
    this.$store.dispatch('simpliciti_map/resetStore', null)
    this.$store.dispatch('search_module/resetStore')
  },
  methods: {
    forceTableReload(interval = 250) {
      let currentmode = this.mode
      if (this.mode !== 'list') {
        console.log(currentmode)
        this.switchMode('list')
        //Must wait nextTick finish before set new switchmode
        setTimeout(() => {
          this.switchMode(currentmode)
        }, interval)
      }
    },
    getFetchEventOptions() {
      let elementIds = []
      let dates = []
      let filters = {}

      if (this.selectedAggregateEventGroups) {
        filters.group = this.selectedAggregateEventGroups
      }

      if (this.detailedItemsBy === 'total') {
        dates = this.datesUsedDuringLastSearch
        elementIds = this.elementIdsUsedDuringLastSearch
      }
      if (this.detailedItemsBy === 'date') {
        dates = [this.detailedItemsByItem.dateItem.id]
        elementIds = this.elementIdsUsedDuringLastSearch
      }
      if (this.detailedItemsBy === 'date-aggregate') {
        /* Event by date grouped by code/type */
        dates = [this.detailedItemsByItem.dateItem.id]
        elementIds = this.elementIdsUsedDuringLastSearch
        filters.type = this.detailedItemsByItem.eventGroup.type
        filters.code = this.detailedItemsByItem.eventGroup.code
      }

      if (this.detailedItemsBy === 'subitem') {
        /* Vehicle */
        dates = [this.detailedItemsByItem.dateItem.id]
        elementIds = [this.detailedItemsByItem.subItem.id]
      }

      if (['total', 'date', 'subitem'].includes(this.detailedItemsBy)) {
        filters.code = [
          ...this.selectedOperationMessageTypes,
          ...this.selectedAnomaliesTypes,
          ...this.selectedStatusTypes,
        ]
      }

      if (this.detailedItemsBy === 'aggregate') {
        /* Event by code/type */
        dates = [this.detailedItemsByItem.dateItem.id]
        elementIds = [this.detailedItemsByItem.subItem.id]
        filters.type = this.detailedItemsByItem.eventGroup.type
        filters.code = this.detailedItemsByItem.eventGroup.code
      }

      dates = dates
        .map((range) => (range instanceof Array ? range[0] : range))
        .sort((a, b) =>
          (a._d || a).getTime() < (b._d || b).getTime() ? 1 : -1
        )

      if (
        this.selectedAnomaliesTypes.length === 0 &&
        this.selectedStatusTypes.length === 0 &&
        this.selectedOperationMessageTypes.length === 0 &&
        filters.type === undefined
      ) {
        filters.type = 0
      }

      return {
        elementIds,
        dates,
        filters,
      }
    },
    /**
     * Fetch events
     * The user can click:
     *  - Buttons in the list - top toolbar (i.g table mode)
     *  - Buttons in the list - date label (i.g table mode)
     *  - Buttons in the list - date label -> event code/type (i.g table mode)
     * @param {*} options
     */
    async fetchEvents() {
      let options = this.getFetchEventOptions()
      let { elementIds, dates, filters } = options
      this.detailedItems = []

      if (this.isFetchDetailsInProgress) {
        this.$log.warn('fetchEvents::skip (in progress)')
        return
      } else {
        this.isFetchDetailsInProgress = true
      }

      fetchEvents({
        elementType: this.activeSearchFormTabName,
        elementIds,
        dates,
        filters,
        handleResults: (results) => {
          this.detailedItems = [...this.detailedItems, ...results]
        },
      }).finally(() => {
        this.isFetchDetailsInProgress = false
      })
    },
    /**
     * There are three possible toolbars actions:
     * map: Switch to table+map
     * table: Siwtch to table
     */
    async onToolbarAction(options) {
      this.detailedItemsBy = options.type
      this.detailedItemsByItem = options.item

      //Async
      this.fetchEvents().finally(() => {
        this.isFetchDetailsInProgress = false
      })

      //Switch layout to map/table
      if (['map', 'table'].includes(options.action)) {
        this.switchMode(
          {
            map: 'table_map',
            table: 'table',
          }[options.action]
        )
      }
    },

    async clearMapMarkers() {
      await this.$store.dispatch('simpliciti_map/setDataset', {
        type: 'eventsMarkers',
        data: [],
      })
    },
    prepareMapMarkers() {
      //Limit map rendering
      if (this.detailedItems.length > 5000) {
        this.$store.dispatch('alert/addAlert', {
          type: 'info',
          title: this.$t('identification.map.render_limit_message_title'),
          text: this.$t('identification.map.render_limit_message_body', {
            length: this.detailedItems.length,
          }),
          //text: `Affichage de 5000 éléments sur les ${this.detailedItems.length} résultats`,
        })
      }

      this.$store.dispatch('simpliciti_map/setDataset', {
        type: 'eventsMarkers',
        data: this.detailedItems.slice(0, 5000),
      })
    },

    /**
     * Fetch synthesis dataset
     * - Splits requests by vehicle/date (1-1)
     */
    async performsSearch() {
      this.clearMapMarkers()
      let dateRanges = this.selection.selectedDateRanges
      //vehiclers or circuits
      let selectedElementsIds = this.$store.getters[
        'search_module/getSelectedIdsByType'
      ](this.activeSearchFormTabName)

      //Test: Predefined selection (berto provence)
      if (getQueryStringValue('test') === '1' && !this.$env.isProduction()) {
        selectedElementsIds = [6447, 29206, 42701]
        dateRanges = [
          [
            moment('2022-04-29T00:00:00Z').minute(0).hour(0).second(0),
            moment('2022-04-29T00:00:00Z').minute(59).hour(23).second(59),
          ],
        ]
      }

      //Validation: Limit vehicles selection
      if (
        selectedElementsIds.length * (dateRanges.length || 1) >
        getEventsSearchSelectionLimit()
      ) {
        this.$alertPopup.showSelectionLimitWarning(
          selectedElementsIds.length * (dateRanges.length || 1),
          getEventsSearchSelectionLimit()
        )
        return
      }

      this.items = [] //This will also triger the results view

      let len = dateRanges.length
      if (len === 0) {
        dateRanges = [
          [
            moment().hour(0).minute(0).second(0)._d,
            moment().hour(23).minute(59).second(59)._d,
          ],
        ]
        len = dateRanges.length
      }
      this.$store.state.search_module.isSearchInProgress = true
      this.$loader && this.$loader.show()

      let dateFrom = dateRanges[0][0]
      let dateTo = dateRanges[len - 1][1]
      this.dateFromFormatted = moment(dateFrom).isValid()
        ? moment(dateFrom).format('DD/MM/YYYY HH:mm:ss')
        : ''
      this.dateToFormatted = moment(dateTo).isValid()
        ? moment(dateTo).format('DD/MM/YYYY HH:mm:ss')
        : ''

      let dates = dateRanges
        .map((range) => range[0])
        .sort((a, b) => (a.getTime() < b.getTime() ? 1 : -1))

      this.elementIdsUsedDuringLastSearch = selectedElementsIds
      this.datesUsedDuringLastSearch = dates

      //TODO: handle pagination using pooling or split requests by elements
      this.items = await fetchEventsCumulation(selectedElementsIds, dates, {
        elementType: this.activeSearchFormTabName,
        filters: {
          ...(this.selectedAnomaliesTypes.length === 0 &&
          this.selectedStatusTypes.length === 0 &&
          this.selectedOperationMessageTypes.length === 0
            ? {
                type: 0,
              }
            : {}),
          //We want to filter by event code (agregat.event.code equals ref.anomalies.code)
          code: this.selectedAnomaliesTypes
            .concat(this.selectedStatusTypes)
            .concat(this.selectedOperationMessageTypes),
        },
      })

      //Switch to results (even if no results from API, user should see the message indicating no results)
      this.$nextTick(() =>
        this.$store.dispatch('search_module/setHasResults', true)
      )

      //Hide loader in search module search button
      this.$store.state.search_module.isSearchInProgress = false
      //Hide big loader
      this.$loader && this.$loader.hide()
    },
    async onSearchModuleSelectionChange() {
      this.performsSearch()
    },
    onSearchModuleViewChange(view) {
      //React to search module view changes (selection / results)
      if (view === 'selection') {
        this.mode = 'list'
      }
    },
    onSearchModuleClearSelection() {
      //Clear custom filters
      this.selectedOperationMessageTypes = []
      this.selectedAnomaliesTypes = []
      this.selectedStatusTypes = []
    },
    switchMode(mode) {
      this.$nextTick(() => {
        this.mode = mode
        if (this.mode === 'list') {
          this.menuCollapsed = false
        } else {
          this.menuCollapsed = true
        }
      })
    },
  },
}
</script>
<style lang="scss" scoped>
.table-layout {
  height: calc(100% - 20px);
}
.filter-label {
  font: normal normal normal 11px/14px Open Sans;
  letter-spacing: 0px;
  color: var(--color-tundora);
  margin: 0px;
}
.radio label {
  font: normal normal normal 11px/14px Open Sans;
  letter-spacing: 0px;
  color: var(--color-tundora);
  margin: 0px;
}
</style>