Source

components/location/LocationCircuitTab/CircuitExecDetails/CircuitExecDetails.vue

<template lang="pug">
.last_circuit_tab
    b-spinner(
        v-if="loading"
        class="loader"
        variant="info"
        style="width: 3rem; height: 3rem; margin: 0 auto; display:block;"
    )
    div(v-if="!loading && (!headInfos || !stepsInfos)") 
      .row
        .col-12
          .alert.alert-info.p-2 {{$t('location_module.no_results')}}
    

    .last_circuit_tab__infos(v-if="!loading && !!headInfos&&!!stepsInfos")
      
      .head_infos(v-if="['circuit','driver'].includes(item.type)" style="justify-content: flex-start;")
        .info_item
            label.info_item_label Vehicle:
            .info_item_value {{item.vehicleName}}
        .info_item
            //@TODO: If no active circuit, print the circuit name here

      .head_infos(v-if="!!headInfos && headInfos.sameDay")
          .info_item(v-show="isSearchByVehicleOrDriver && headInfos.circuitName")
            label.info_item_label {{$t('common.Circuit')}}:
            .info_item_value {{headInfos.circuitName}}
          .info_item()
              label.info_item_label {{$t('common.date')}}:
              .info_item_value {{headInfos.dateFrom}}
          .info_item
              label.info_item_label {{$t('common.debut')}}:
              .info_item_value {{headInfos.timeFrom}}
          .info_item(v-show="!hasActiveCircuit")
              label.info_item_label {{$t('common.fin')}}:
              .info_item_value {{headInfos.timeTo}}
      .head_infos(v-if="!!headInfos && !headInfos.sameDay")
          .info_item
              label.info_item_label {{$t('common.debut')}}:
              .info_item_value {{headInfos.dateFrom}} {{headInfos.timeFrom}}
          .info_item(v-show="!hasActiveCircuit")
              label.info_item_label {{$t('common.fin')}}:
              .info_item_value {{headInfos.dateTo}} {{headInfos.timeTo}}
      .head_infos(v-if="!!headInfos")
          .info_item
              label.info_item_label {{$t('common.distance')}}:
              .info_item_value {{headInfos.distance}}Km
          .info_item
              label.info_item_label {{$t('common.Réalisation')}}:
              .info_item_value {{headInfos.donePercentage}}%
          .info_item
      .row
        .col

      .row
        .col.steps_filter_wrapper()
          CircuitExecutionStepsFilter(v-model="stepsFilter")
        .col
          .toolbar(v-if="!!stepsInfos")
            
            LocationExportPendulumButton(v-show="$store.getters['location_module/selectedItem'].circuitExecutionId"
            fontSize="18px"
            )

            div(v-b-tooltip.hover :title="$t('location.circuit.button_load_gps_positions')")
              b-btn.table_mode_btn.mini-btn(variant="outline-primary" 
                @click="()=>$emit('loadGPSPositions')"
                :disabled="isLoadingGPSPositions"
              ) 
                //em.fa.fa-map-pin(style="margin-right:0px!important;")
                simple-svg(
                    width="15px"
                    height="15px"
                    fill="var(--color-dark-blue)" fillClassName="cls-1"
                    :src="require('./assets/view-circ-exec-gps-positions.svg')"
                )
            
            div(v-b-tooltip.hover :title="$t('location.circuit.button_load_trip_history')")
              b-btn.table_mode_btn.mini-btn(variant="outline-primary" 
                @click="(e)=>$emit('loadTripHistory') || e.target.blur()"
                :disabled="isLoadingTripHistory"
              ) 
                simple-svg(
                    width="15px"
                    height="15px"
                    fill="var(--color-dark-blue)" fillClassName="cls-1"
                    :src="require('./assets/view-trip-history.svg')"
                )

            //@TODO: refactor into shared/LayoutSwitchModeButton.vue
            TableButton(
              :title="$t('common.layout.modes.table_and_map_button_tooltip')"
              @click="switchToTablePlusMapMode({bottom:'CircuitExecTable',top:'CircuitExecMap',bottomProps})"
              icon="table-map")
      VerticalChart(v-if="!!stepsInfos" :nodes="stepsInfos"
        @nodeclick="nodeclick"
        @nodeclickright="nodeclickright"
        :activeNodeId="activeNodeId"
      )
          template(v-slot:node_icon="slotProps")
              NodeIcon(:color="slotProps.node.color")
          template(v-slot:node_content="slotProps")
              span(v-if="slotProps.node.date") {{slotProps.node.date}}
              
              .row.m-0
                .col-6.p-0
                  .label {{$t('location.details.circuit_tab.label_step_number')}} {{slotProps.node.id}}
                .col-6.p-0
                  .label {{$t('location.details.circuit_tab.label_distance')}}: 
                    span.value {{(slotProps.node.distance_theorique*1000).toFixed(2)}}m
              .row.m-0
                .col-12.p-0
                  .label {{$t('location.details.circuit_tab.label_action')}}: 
                    span.value {{slotProps.node.activite_nom}}
              .row.m-0
                .col-6.p-0()
                  .label {{$t('location.details.circuit_tab.label_duration')}}: 
                    span.value {{slotProps.node.duration}}
                .col-6.p-0
                  .label {{$t('location.details.circuit_tab.label_done_perc')}}: 
                    em.fa.fa-check(v-show="slotProps.node.realise" style="color:var(--color-silver-tree)") 
                    em.fa.fa-times(v-show="!slotProps.node.realise" style="color:var(--color-coral-red)") 

  
</template>
<script>
import Vue from 'vue'
import moment from 'moment'
import VerticalChart from '@c/shared/SearchResults/Submenu/VerticalChart.vue'
import { mapGetters } from 'vuex'
import NodeIcon from '@c/shared/SearchResults/Submenu/CircuitExecutionStepIcon.vue'
import TableButton from '@c/shared/TableButton.vue'
import MapIcon from '@c/shared/TableModesToolbar/MapIcon.vue'
import { formatSeconds } from '@/utils/dates.js'
import CircuitExecutionStepsFilter from '@c/shared/CircuitExecutionStepsFilter.vue'
import { createCircuitExecutionStepsFilter } from '@c/location/mixins/circuit-execution-mixin.js'
import locationLayoutModesMixin from '@c/location/mixins/location-layout-modes.js'
import { APIV2RequestDatetimeFormat } from '@/config/simpliciti-apis.js'
import LocationExportPendulumButton from '@c/location/LocationExportPendulumButton.vue'

function getFormattedTimeFromApiDate(date) {
  let m = moment(date, APIV2RequestDatetimeFormat)
  return (m.isValid() && Vue.$date.formatTimeWithSeconds(m)) || '...'
}

/**
 * Used by
 * Main search - history by circuit - circuit tab
 * Main search - realtime by [all] - circuit tab (last circuit)
 * @namespace components
 * @category components
 * @subcategory location/circuit
 * @module CircuitExecDetails
 **/
export default {
  name: 'CircuitExecDetails',
  components: {
    CircuitExecutionStepsFilter,
    VerticalChart,
    NodeIcon,
    TableButton,
    MapIcon,
    LocationExportPendulumButton,
  },
  mixins: [locationLayoutModesMixin],
  inject: {
    isLoadingGPSPositions: {
      default: false,
    },
    isLoadingTripHistory: {
      default: false,
    },
  },
  props: {
    /**
     * selected item (From real-time/history search)
     */
    item: {
      type: Object,
      default: () => ({}),
    },
    /**
     * Used by: Location - History - Circuit tab - (Multiple circuit executions for the same vehicle)
     */
    circuitDetailsFromAPI: {
      type: Object,
      default: () => null,
    },
    /**
     * Used by: Location - History - Circuit tab - (Multiple circuit executions for the same vehicle)
     */
    circuitExecutionStepsFromAPI: {
      type: Object,
      default: () => null,
    },
  },
  data() {
    return {
      loading: false,
      activeNodeId: null,
      stepsFilter: 'EXECUTION',
    }
  },
  computed: {
    ...mapGetters({
      circuitDetailsFromStore: 'location_module/circuitDetails',
      circuitExecutionStepsFromStore: 'location_module/circuitExecutionSteps',
    }),
    bottomProps() {
      return {
        circuitExecutionSteps: this.circuitExecutionSteps,
        circuitDetails: this.circuitDetails,
      }
    },
    circuitDetails() {
      return this.circuitDetailsFromAPI || this.circuitDetailsFromStore
    },
    circuitExecutionSteps() {
      return (
        this.circuitExecutionStepsFromAPI || this.circuitExecutionStepsFromStore
      )
    },
    headInfos() {
      const item = this.circuitDetails
      return (
        (item &&
          !!item.circuit_id &&
          Object.keys(item).length > 0 && {
            circuitName: item.circuit_nom_court || '',
            dateFrom: this.$date.formatDate(item.dateFrom),
            timeFrom: getFormattedTimeFromApiDate(item.dateFrom),
            dateTo: this.$date.formatDate(item.dateTo),
            timeTo: getFormattedTimeFromApiDate(item.dateTo),
            sameDay: moment(item.dateFrom).isSame(moment(item.dateTo), 'day'),
            distance: item.lg_realisee_circuit || '...',
            donePercentage: item.taux_realisation_circuit || '...',
          }) ||
        null
      )
    },
    isSearchByVehicleOrDriver() {
      return ['vehicle', 'driver'].includes(this.item.type)
    },
    unfilteredStepsInfos() {
      return (this.circuitExecutionSteps.troncons || []).map((t) =>
        Object.freeze({
          ...t,
          date: this.$date.formatTime(t.dateheure_debut, {
            fallbackValue: '...',
          }),
          duration: formatSeconds(t.duree_sec),
          color: t.realise ? t.activite_couleur || '#70BD95' : '#FF4545',
        })
      )
    },
    stepsInfos() {
      return (
        (this.unfilteredStepsInfos || []).filter(
          createCircuitExecutionStepsFilter(this.stepsFilter)
        ) || []
      )
    },
    hasActiveCircuit() {
      return this.item.searchType === 'realtime' && !!this.item.circuitId
    },
  },
  watch: {
    item() {
      if (!this.circuitDetailsFromAPI) {
        this.updateUsingVuex()
      }
    },
  },
  async mounted() {
    this.switchToListMode({
      mapComponent: 'CircuitExecMap',
    })

    if (!this.circuitDetailsFromAPI) {
      this.updateUsingVuex()
    }
  },
  destroyed() {
    this.$store.dispatch(
      'simpliciti_map/highlightCircuitExecPolylineSection',
      null
    )
  },
  methods: {
    async updateUsingVuex() {
      if (this.loading) {
        return false
      }
      this.loading = true
      await this.$store.dispatch(
        'location_module/updateCircuitExecutionDetails',
        {
          vehicleId: this.item.vehicleId,
        }
      )
      this.loading = false
    },
    nodeclick(node) {
      if (this.activeNodeId) {
        this.activeNodeId = null
        this.$store.dispatch(
          'simpliciti_map/highlightCircuitExecPolylineSection',
          null
        )
        return
      }
      this.$store.dispatch(
        'simpliciti_map/highlightCircuitExecPolylineSection',
        node.id
      )
      this.$log.debug('nodeclick', node.id)
      this.activeNodeId = node.id
    },
    nodeclickright({ event, node }) {
      event.stopPropagation()
      this.activeNodeId = node.id
      this.$store.dispatch(
        'simpliciti_map/highlightCircuitExecPolylineSection',
        node.id
      )

      this.$mitt.emit('SIMPLICITI_MAP__FLY_TO_POLYLINE', {
        stepNumber: node.id,
      })

      this.$log.debug('nodeclickright', node.id)
    },
  },
}
</script>
<style lang="scss" scoped>
.info_item_value {
  text-align: left;
  font: normal normal normal 12px/18px Open Sans;
  letter-spacing: 0px;
  color: var(--color-tundora);
}
.info_item_label {
  text-align: left;
  font: normal normal normal 11px/18px Open Sans;
  letter-spacing: 0px;
  color: var(--color-metal-rock);
  margin-right: 5px;
}
.info_item {
  display: flex;
  flex-grow: 1;
  min-width: 41px;
}
.head_infos {
  display: flex;
  justify-content: flex-start;
  column-gap: 5px;
  margin: 0px 5px;
  flex-wrap: wrap;
}
.toolbar {
  display: flex;
  justify-content: flex-end;
  column-gap: 5px;
  align-items: center;
}
.node,
.node_content {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}
.node_content {
  flex-direction: column;
}
.node_union {
  width: 4px;
  background-color: var(--color-dark-blue);
  height: 1px;
  margin-left: 10px;
}
.steps_filter_wrapper {
  display: flex;
  align-items: center;
}
</style>