view client/src/components/Bottlenecks.vue @ 2462:9ae2a2f758bb

client: make use of new table header/body components
author Markus Kottlaender <markus@intevation.de>
date Mon, 04 Mar 2019 14:50:23 +0100
parents 64ff5984351e
children 468c8dc796cf
line wrap: on
line source

<template>
  <div>
    <UIBoxHeader
      icon="ship"
      title="Bottlenecks"
      :closeCallback="$parent.close"
    />
    <UITableHeader
      :columns="[
        { id: 'name', title: 'Name', class: 'col-4' },
        {
          id: 'latestMeasurement',
          title: 'Latest Measurement',
          class: 'col-3'
        },
        { id: 'chainage', title: 'Chainage', class: 'col-3' }
      ]"
      @sortingChanged="sortBy"
    />
    <UITableBody
      :data="filteredAndSortedBottlenecks()"
      :maxHeight="(showSplitscreen ? 18 : 35) + 'rem'"
      :active="openBottleneck"
      v-slot="{ item: bottleneck }"
    >
      <div class="col-4 py-2 text-left">
        <a href="#" @click="selectBottleneck(bottleneck)">{{
          bottleneck.properties.name
        }}</a>
      </div>
      <div class="col-3 py-2">
        {{ formatSurveyDate(bottleneck.properties.current) }}
      </div>
      <div class="col-3 py-2">
        {{
          displayCurrentChainage(
            bottleneck.properties.from,
            bottleneck.properties.to
          )
        }}
      </div>
      <div class="col-2 pr-0 text-right d-flex flex-column">
        <a
          class="text-info mt-auto mb-auto mr-2"
          @click="loadSurveys(bottleneck)"
          v-if="bottleneck.properties.current"
        >
          <font-awesome-icon
            class="pointer"
            icon="spinner"
            fixed-width
            spin
            v-if="loading === bottleneck"
          ></font-awesome-icon>
          <font-awesome-icon
            class="pointer"
            icon="angle-down"
            fixed-width
            v-if="loading !== bottleneck && openBottleneck !== bottleneck"
          ></font-awesome-icon>
          <font-awesome-icon
            class="pointer"
            icon="angle-up"
            fixed-width
            v-if="loading !== bottleneck && openBottleneck === bottleneck"
          ></font-awesome-icon>
        </a>
      </div>
      <div
        :class="[
          'col-12 p-0',
          'surveys',
          { open: openBottleneck === bottleneck }
        ]"
      >
        <a
          href="#"
          class="d-inline-block px-3 py-2"
          v-for="(survey, index) in openBottleneckSurveys"
          :key="index"
          @click="selectSurvey(survey, bottleneck)"
        >
          {{ formatSurveyDate(survey.date_info) }}
        </a>
      </div>
    </UITableBody>
  </div>
</template>

<style lang="sass" scoped>
.table-body
  .row
    > div:not(:last-child)
      transition: background-color 0.3s, color 0.3s
    &.active
      > div:not(:last-child)
        background-color: $color-info
        color: #fff
        a
          color: #fff !important
      .surveys
        border-bottom: solid 1px $color-info
    .surveys
      overflow: hidden
      max-height: 0
      &.open
        overflow-y: auto
        max-height: 5rem
</style>

<script>
/* This is Free Software under GNU Affero General Public License v >= 3.0
 * without warranty, see README.md and license for details.
 *
 * SPDX-License-Identifier: AGPL-3.0-or-later
 * License-Filename: LICENSES/AGPL-3.0.txt
 *
 * Copyright (C) 2018 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Markus Kottländer <markus.kottlaender@intevation.de>
 */
import { mapState } from "vuex";
import { HTTP } from "@/lib/http";
import { displayError } from "@/lib/errors.js";
import { formatSurveyDate } from "@/lib/date.js";

export default {
  name: "bottlenecks",
  data() {
    return {
      sortColumn: "name",
      sortDirection: "ASC",
      openBottleneck: null,
      openBottleneckSurveys: null,
      loading: null
    };
  },
  computed: {
    ...mapState("application", [
      "searchQuery",
      "showSearchbarLastState",
      "showSplitscreen"
    ]),
    ...mapState("bottlenecks", ["bottlenecksList"]),
    sortIcon() {
      return this.sortDirection === "ASC"
        ? "sort-amount-down"
        : "sort-amount-up";
    }
  },
  methods: {
    formatSurveyDate(date) {
      return formatSurveyDate(date);
    },
    filteredAndSortedBottlenecks() {
      return this.bottlenecksList
        .filter(bn => {
          return bn.properties.name
            .toLowerCase()
            .includes(this.searchQuery.toLowerCase());
        })
        .sort((bnA, bnB) => {
          switch (this.sortColumn) {
            case "name":
              if (
                bnA.properties.name.toLowerCase() <
                bnB.properties.name.toLowerCase()
              )
                return this.sortDirection === "ASC" ? -1 : 1;
              if (
                bnA.properties.name.toLowerCase() >
                bnB.properties.name.toLowerCase()
              )
                return this.sortDirection === "ASC" ? 1 : -1;
              return 0;

            case "latestMeasurement": {
              if (
                (bnA.properties.current || "") < (bnB.properties.current || "")
              )
                return this.sortDirection === "ASC" ? -1 : 1;
              if (
                (bnA.properties.current || "") > (bnB.properties.current || "")
              )
                return this.sortDirection === "ASC" ? 1 : -1;
              return 0;
            }

            case "chainage":
              if (bnA.properties.from < bnB.properties.from)
                return this.sortDirection === "ASC" ? -1 : 1;
              if (bnA.properties.from > bnB.properties.from)
                return this.sortDirection === "ASC" ? 1 : -1;
              return 0;

            default:
              return 0;
          }
        });
    },
    selectSurvey(survey, bottleneck) {
      this.$store
        .dispatch(
          "bottlenecks/setSelectedBottleneck",
          bottleneck.properties.name
        )
        .then(() => {
          this.$store.commit("bottlenecks/selectedSurvey", survey);
        })
        .then(() => {
          this.$store.commit("map/moveToExtent", {
            feature: bottleneck,
            zoom: 17,
            preventZoomOut: true
          });
        });
    },
    selectBottleneck(bottleneck) {
      this.$store
        .dispatch(
          "bottlenecks/setSelectedBottleneck",
          bottleneck.properties.name
        )
        .then(() => {
          this.$store.commit("bottlenecks/setFirstSurveySelected");
        })
        .then(() => {
          this.$store.commit("map/moveToExtent", {
            feature: bottleneck,
            zoom: 17,
            preventZoomOut: true
          });
        });
    },
    sortBy(sorting) {
      this.sortColumn = sorting.sortColumn;
      this.sortDirection = sorting.sortDirection;
    },
    loadSurveys(bottleneck) {
      if (bottleneck === this.openBottleneck) {
        this.openBottleneck = null;
        this.openBottleneckSurveys = null;
      } else {
        this.loading = bottleneck;

        HTTP.get("/surveys/" + bottleneck.properties.name, {
          headers: {
            "X-Gemma-Auth": localStorage.getItem("token"),
            "Content-type": "text/xml; charset=UTF-8"
          }
        })
          .then(response => {
            this.openBottleneckSurveys = response.data.surveys.sort((a, b) => {
              return a.date_info < b.date_info ? 1 : -1;
            });
            this.openBottleneck = bottleneck;
          })
          .catch(error => {
            const { status, data } = error.response;
            displayError({
              title: this.$gettext("Backend Error"),
              message: `${status}: ${data.message || data}`
            });
          })
          .finally(() => (this.loading = null));
      }
    },
    displayCurrentChainage(from, to) {
      return from / 10 + " - " + to / 10;
    }
  },
  mounted() {
    this.$store.dispatch("bottlenecks/loadBottlenecksList");
  }
};
</script>