view client/src/components/Bottlenecks.vue @ 2739:8057662812f1

client: fixed active state styling in users and bottlenecks table
author Markus Kottlaender <markus@intevation.de>
date Tue, 19 Mar 2019 19:09:41 +0100
parents add2d47c2567
children c6fba10926cc
line wrap: on
line source

<template>
  <div>
    <UIBoxHeader
      icon="ship"
      title="Bottlenecks"
      :closeCallback="$parent.close"
    />
    <UITableHeader
      :columns="[
        { id: 'properties.name', title: 'Name', class: 'col-4' },
        {
          id: 'properties.current',
          title: 'Latest Measurement',
          class: 'col-3'
        },
        { id: 'properties.from', title: 'Chainage', class: 'col-3' }
      ]"
    />
    <UITableBody
      :data="filteredBottlenecks() | sortTable(sortColumn, sortDirection)"
      :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">
        {{ bottleneck.properties.current | surveyDate }}
      </div>
      <div class="col-3 py-2">
        {{
          displayCurrentChainage(
            bottleneck.properties.from,
            bottleneck.properties.to
          )
        }}
      </div>
      <div class="col-2 py-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)"
        >
          {{ survey.date_info | surveyDate }}
        </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";
import { sortTable } from "@/lib/mixins";

export default {
  name: "bottlenecks",
  mixins: [sortTable],
  data() {
    return {
      sortColumn: "properties.name",
      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: {
    filteredBottlenecks() {
      return this.bottlenecksList.filter(bn => {
        return bn.properties.name
          .toLowerCase()
          .includes(this.searchQuery.toLowerCase());
      });
    },
    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
          });
        });
    },
    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>