diff 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 diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/Bottlenecks.vue	Wed Dec 12 09:22:20 2018 +0100
@@ -0,0 +1,324 @@
+<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>