view client/src/components/Bottlenecks.vue @ 1558:0ded4c56978e

refac: component filestructure. remove admin/map hierarchy
author Thomas Junk <thomas.junk@intevation.de>
date Wed, 12 Dec 2018 09:22:20 +0100
parents client/src/components/map/contextbox/Bottlenecks.vue@276df8dadc14
children 70421380142d
line wrap: on
line source

<template>
  <div>
    <h6 class="mb-0 py-2 px-3 border-bottom d-flex align-items-center">
      <font-awesome-icon icon="ship" class="mr-2"></font-awesome-icon>
      <translate>Bottlenecks</translate>
    </h6>
    <div class="row p-2 text-left small">
      <div class="col-5">
        <a href="#" @click="sortBy('name')" class="sort-link">
          <translate>Name</translate>
        </a>
        <font-awesome-icon
          :icon="sortIcon"
          class="ml-1"
          v-if="sortColumn === 'name'"
        ></font-awesome-icon>
      </div>
      <div class="col-2">
        <a href="#" @click="sortBy('latestMeasurement')" class="sort-link">
          <translate>Latest</translate> <br />
          <translate>Measurement</translate>
        </a>
        <font-awesome-icon
          :icon="sortIcon"
          class="ml-1"
          v-if="sortColumn === 'latestMeasurement'"
        ></font-awesome-icon>
      </div>
      <div class="col-3">
        <a href="#" @click="sortBy('chainage')" class="sort-link">
          <translate>Chainage</translate>
        </a>
        <font-awesome-icon
          :icon="sortIcon"
          class="ml-1"
          v-if="sortColumn === 'chainage'"
        ></font-awesome-icon>
      </div>
      <div class="col-2"></div>
    </div>
    <div
      class="bottleneck-list small text-left"
      :style="'max-height: ' + (showSplitscreen ? 18 : 35) + 'rem'"
      v-if="filteredAndSortedBottlenecks().length"
    >
      <div
        v-for="bottleneck in filteredAndSortedBottlenecks()"
        :key="bottleneck.properties.name"
        class="border-top row bottleneck-row mx-0"
      >
        <div class="col-5 py-2 text-left">
          <a href="#" @click="selectBottleneck(bottleneck)">{{
            bottleneck.properties.name
          }}</a>
        </div>
        <div class="col-2 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">
          <button
            type="button"
            class="btn btn-sm btn-info rounded-0 h-100"
            @click="loadSurveys(bottleneck.properties.name)"
            v-if="bottleneck.properties.current"
          >
            <font-awesome-icon
              icon="spinner"
              fixed-width
              spin
              v-if="loading === bottleneck.properties.name"
            ></font-awesome-icon>
            <font-awesome-icon
              icon="angle-down"
              fixed-width
              v-if="
                loading !== bottleneck.properties.name &&
                  openBottleneck !== bottleneck.properties.name
              "
            ></font-awesome-icon>
            <font-awesome-icon
              icon="angle-up"
              fixed-width
              v-if="
                loading !== bottleneck.properties.name &&
                  openBottleneck === bottleneck.properties.name
              "
            ></font-awesome-icon>
          </button>
        </div>
        <div
          :class="[
            'col-12 p-0',
            'surveys',
            { open: openBottleneck === bottleneck.properties.name }
          ]"
        >
          <a
            href="#"
            class="d-block px-3 py-2"
            v-for="(survey, index) in openBottleneckSurveys"
            :key="index"
            @click="selectSurvey(survey, bottleneck)"
            >{{ formatSurveyDate(survey.date_info) }}</a
          >
        </div>
      </div>
    </div>
    <div v-else class="small text-center py-3 border-top">
      <translate>No results.</translate>
    </div>
  </div>
</template>

<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", ["bottlenecks"]),
    sortIcon() {
      return this.sortDirection === "ASC"
        ? "sort-amount-down"
        : "sort-amount-up";
    }
  },
  methods: {
    formatSurveyDate(date) {
      return formatSurveyDate(date);
    },
    filteredAndSortedBottlenecks() {
      return this.bottlenecks
        .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/moveMap", {
            coordinates: bottleneck.geometry.coordinates,
            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/moveMap", {
            coordinates: bottleneck.geometry.coordinates,
            zoom: 17,
            preventZoomOut: true
          });
        });
    },
    sortBy(column) {
      this.sortColumn = column;
      this.sortDirection = this.sortDirection === "ASC" ? "DESC" : "ASC";
    },
    loadSurveys(name) {
      this.openBottleneckSurveys = null;
      if (name === this.openBottleneck) {
        this.openBottleneck = null;
      } else {
        this.openBottleneck = name;
        this.loading = name;

        HTTP.get("/surveys/" + 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;
            });
          })
          .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/loadBottlenecks");
  }
};
</script>

<style lang="scss" scoped>
.bottleneck-list {
  overflow-y: auto;
}

.bottleneck-list .bottleneck-row a {
  text-decoration: none;
}

.bottleneck-list .bottleneck-row:hover {
  background: #fbfbfb;
}

.surveys {
  max-height: 0;
  min-height: 0;
  overflow: hidden;
}

.surveys a:hover {
  background: #f3f3f3;
}

.surveys.open {
  max-height: 250px;
  overflow: auto;
}

.sort-link {
  color: #444;
  font-weight: bold;
}
</style>