changeset 2417:e02abe7f16c2 staging_consolidation

remove old components importqueue,staging
author Thomas Junk <thomas.junk@intevation.de>
date Thu, 28 Feb 2019 13:47:53 +0100
parents 706121185e63
children 39df9967343c
files client/src/components/importqueue/Importqueue.vue client/src/components/importqueue/Importqueuedetail.vue client/src/components/staging/Staging.vue client/src/components/staging/StagingDetail.vue
diffstat 4 files changed, 0 insertions(+), 1486 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/importqueue/Importqueue.vue	Thu Feb 28 13:46:09 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,351 +0,0 @@
-<template>
-  <div class="d-flex flex-row">
-    <Spacer></Spacer>
-    <div class="mt-3 importqueuecard flex-grow-1">
-      <div class="card shadow-xs">
-        <UIBoxHeader icon="tasks" title="Importqueue" />
-        <div class="card-body importcardbody">
-          <div class="card-body importcardbody">
-            <div class="searchandfilter d-flex flex-row">
-              <div class="searchgroup input-group">
-                <div class="input-group-prepend">
-                  <span class="input-group-text" id="search">
-                    <font-awesome-icon icon="search"></font-awesome-icon>
-                  </span>
-                </div>
-                <input
-                  v-model="searchQuery"
-                  type="text"
-                  class="form-control"
-                  placeholder
-                  aria-label="Search"
-                  aria-describedby="search"
-                />
-              </div>
-              <div class="filters">
-                <button @click="setFilter('failed')" :class="failedStyle">
-                  <translate>Failed</translate>
-                </button>
-                <button @click="setFilter('pending')" :class="pendingStyle">
-                  <translate>Pending</translate>
-                </button>
-                <button @click="setFilter('rejected')" :class="rejectedStyle">
-                  <translate>Rejected</translate>
-                </button>
-                <button @click="setFilter('accepted')" :class="acceptedStyle">
-                  <translate>Accepted</translate>
-                </button>
-                <button @click="setFilter('warning')" :class="warningStyle">
-                  <translate>Warning</translate>
-                </button>
-              </div>
-            </div>
-            <div class="text-left d-flex flex-row border-bottom entries">
-              <div class="header py-1 jobid mr-2">
-                <translate>Id</translate>
-              </div>
-              <div class="header py-1 enqueued mr-2">
-                <translate>Enqueued</translate>
-              </div>
-              <div class="header py-1 kind mr-2">
-                <translate>Kind</translate>
-              </div>
-              <div class="header py-1 user mr-2">
-                <translate>User</translate>
-              </div>
-              <div class="header py-1 signer mr-2">
-                <translate>Signer</translate>
-              </div>
-              <div class="header py-1 state mr-2">
-                <translate>State</translate>
-              </div>
-            </div>
-            <div class="importqueuedetail">
-              <div
-                class="text-left"
-                v-for="job in filteredImports"
-                :key="job.id"
-              >
-                <Importqueuedetail
-                  :reload="reload"
-                  :job="job"
-                ></Importqueuedetail>
-              </div>
-            </div>
-            <div>
-              <button @click="refresh" class="btn btn-info refresh">
-                <translate>Refresh</translate>
-              </button>
-            </div>
-          </div>
-        </div>
-      </div>
-    </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@intevation.de>
- */
-import { displayError } from "@/lib/errors.js";
-import { mapState } from "vuex";
-import { HTTP } from "@/lib/http.js";
-
-export default {
-  name: "importqueue",
-  components: {
-    Importqueuedetail: () => import("./Importqueuedetail"),
-    Spacer: () => import("@/components/Spacer")
-  },
-  data() {
-    return {
-      reload: false,
-      searchQuery: "",
-      successful: false,
-      failed: false,
-      pending: false,
-      rejected: false,
-      accepted: false,
-      warning: false
-    };
-  },
-  mounted() {
-    this.loadQueue();
-  },
-  methods: {
-    setFilter(name) {
-      this[name] = !this[name];
-      const allSet =
-        this.failed &&
-        this.pending &&
-        this.accepted &&
-        this.rejected &&
-        this.warning;
-      if (allSet) {
-        this.warning = false;
-        this.successful = false;
-        this.failed = false;
-        this.pending = false;
-        this.accepted = false;
-        this.rejected = false;
-      }
-    },
-    loadQueue() {
-      this.reload = true;
-      this.$store
-        .dispatch("imports/getImports")
-        .then(() => {
-          this.reload = false;
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    },
-    refresh() {
-      this.loadQueue();
-    },
-    showDetails(id) {
-      HTTP.get("/imports/" + id, {
-        headers: { "X-Gemma-Auth": localStorage.getItem("token") }
-      })
-        .then(response => {
-          const { entries } = response.data;
-          this.entries = entries;
-          this.$modal.show("details");
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    },
-    close() {
-      this.$modal.hide("details");
-    }
-  },
-  computed: {
-    ...mapState("imports", ["imports"]),
-    ...mapState("application", ["showSidebar"]),
-    sortIcon() {
-      return this.sortAsc ? "sort-amount-down" : "sort-amount-up";
-    },
-    filteredImports() {
-      const filtered = this.imports
-        .filter(element => {
-          if (!this.searchQuery) return true;
-          return [(element.kind, element.user, element.enqueued)].some(x => {
-            return x.toLowerCase().includes(this.searchQuery.toLowerCase());
-          });
-        })
-        .filter(y => {
-          if (
-            !this.failed &&
-            !this.pending &&
-            !this.accepted &&
-            !this.rejected &&
-            !this.warning
-          )
-            return true;
-          let filterCriteria = [];
-          if (this.failed) filterCriteria.push("failed");
-          if (this.pending) filterCriteria.push("pending");
-          if (this.accepted) filterCriteria.push("accepted");
-          if (this.rejected) filterCriteria.push("declined");
-          const result = filterCriteria.map(selectedState => {
-            return y.state === selectedState;
-          });
-          if (this.warning) return result.some(x => x) || y.warnings;
-          return result.some(x => x);
-        });
-      return filtered;
-    },
-    pendingStyle() {
-      return {
-        btn: true,
-        "btn-light": !this.pending,
-        "btn-dark": this.pending
-      };
-    },
-    failedStyle() {
-      return {
-        btn: true,
-        "btn-light": !this.failed,
-        "btn-dark": this.failed
-      };
-    },
-    rejectedStyle() {
-      return {
-        btn: true,
-        "btn-light": !this.rejected,
-        "btn-dark": this.rejected
-      };
-    },
-    acceptedStyle() {
-      return {
-        btn: true,
-        "btn-light": !this.accepted,
-        "btn-dark": this.accepted
-      };
-    },
-    warningStyle() {
-      return {
-        btn: true,
-        "btn-light": !this.warning,
-        "btn-dark": this.warning
-      };
-    }
-  }
-};
-</script>
-
-<style lang="scss" scoped>
-.importqueuedetail {
-  margin-bottom: 3rem;
-}
-.entries {
-  width: 100%;
-}
-
-.jobid {
-  width: 15%;
-}
-
-.enqueued {
-  width: 15%;
-}
-
-.user {
-  width: 15%;
-}
-
-.signer {
-  width: 15%;
-}
-
-.kind {
-  width: 10%;
-}
-
-.state {
-  width: 15%;
-}
-
-.details thead {
-  display: block;
-}
-.details tbody {
-  display: block;
-}
-
-.details tbody {
-  height: 260px;
-  overflow-y: auto;
-  overflow-x: hidden;
-}
-
-.closebutton {
-  top: $small-offset;
-}
-
-.refresh {
-  position: absolute;
-  right: $offset;
-  bottom: $offset;
-}
-
-.importqueuecard {
-  width: 97%;
-  margin-right: $offset;
-  min-height: 20rem;
-}
-
-.card-body {
-  width: 100%;
-  margin-left: auto;
-  margin-right: auto;
-}
-
-.searchandfilter {
-  position: relative;
-  margin-bottom: $xx-large-offset;
-}
-
-.filters {
-  position: absolute;
-  right: 0;
-}
-
-.filters button {
-  margin-right: $small-offset;
-}
-
-.table td,
-.table th {
-  border-top: 0 !important;
-  text-align: left;
-  padding: $small-offset !important;
-}
-
-.searchgroup {
-  position: absolute;
-  left: 0;
-  width: 45%;
-}
-</style>
--- a/client/src/components/importqueue/Importqueuedetail.vue	Thu Feb 28 13:46:09 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,359 +0,0 @@
-<template>
-  <div class="entry d-flex flex-column py-1 border-bottom">
-    <div class="d-flex flex-row position-relative">
-      <div @click="showDetails(job.id)" class="jobid ml-2 mt-1 mr-2">
-        {{ job.id }}
-      </div>
-      <div @click="showDetails(job.id)" class="enqueued mt-1  mr-2">
-        {{ formatDateTime(job.enqueued) }}
-      </div>
-      <div @click="showDetails(job.id)" class="kind mt-1 mr-2">
-        {{ job.kind.toUpperCase() }}
-      </div>
-      <div @click="showDetails(job.id)" class="user mt-1 mr-2">
-        {{ job.user }}
-      </div>
-      <div @click="showDetails(job.id)" class="signer mt-1 mr-2">
-        {{ job.signer }}
-      </div>
-      <div @click="showDetails(job.id)" class="state mt-1 mr-2">
-        <span :class="{ 'text-danger': job.state.toUpperCase() == 'FAILED' }"
-          >{{ job.state
-          }}<font-awesome-icon
-            v-if="job.warnings"
-            class="ml-1 text-warning"
-            icon="exclamation-triangle"
-            fixed-width
-          ></font-awesome-icon
-        ></span>
-      </div>
-      <div @click="showDetails(job.id)" class="mt-1 text-info detailsbutton">
-        <font-awesome-icon
-          class="pointer"
-          v-if="show"
-          icon="angle-up"
-          fixed-width
-        ></font-awesome-icon>
-        <font-awesome-icon
-          class="pointer"
-          v-if="loading"
-          icon="spinner"
-          fixed-width
-        ></font-awesome-icon>
-        <font-awesome-icon
-          class="pointer"
-          v-if="!show && !loading"
-          icon="angle-down"
-          fixed-width
-        ></font-awesome-icon>
-      </div>
-    </div>
-    <div class="detailstable d-flex flex-row">
-      <div :class="collapse">
-        <table class="table table-responsive">
-          <thead>
-            <tr>
-              <th class="type pb-0">
-                <small class="condensed"><translate>Kind</translate></small>
-              </th>
-              <th class="datetime  pb-0">
-                <a href="#" @click="sortAsc = !sortAsc" class="sort-link"
-                  ><small class="condensed"><translate>Date</translate></small>
-                  <small class="condensed"
-                    ><font-awesome-icon
-                      :icon="sortIcon"
-                      class="ml-1"
-                    ></font-awesome-icon></small
-                ></a>
-              </th>
-              <th class="message pb-0">
-                <small class="condensed"><translate>Message</translate></small>
-              </th>
-            </tr>
-          </thead>
-          <tbody>
-            <tr
-              v-for="(entry, index) in sortedEntries"
-              :key="index"
-              class="detailsrow"
-            >
-              <td class="type">
-                <span
-                  :class="[
-                    'condensed',
-                    {
-                      'text-danger': entry.kind.toUpperCase() == 'ERROR',
-                      'text-warning': entry.kind.toUpperCase() == 'WARN'
-                    }
-                  ]"
-                  >{{ entry.kind.toUpperCase() }}</span
-                >
-              </td>
-              <td class="datetime">
-                <span
-                  :class="[
-                    'condensed',
-                    {
-                      'text-danger': entry.kind.toUpperCase() == 'ERROR',
-                      'text-warning': entry.kind.toUpperCase() == 'WARN'
-                    }
-                  ]"
-                  >{{ formatDateTime(entry.time) }}</span
-                >
-              </td>
-              <td class="message">
-                <span
-                  :class="[
-                    'condensed',
-                    {
-                      'text-danger': entry.kind.toUpperCase() == 'ERROR',
-                      'text-warning': entry.kind.toUpperCase() == 'WARN'
-                    }
-                  ]"
-                  >{{ entry.message }}</span
-                >
-              </td>
-            </tr>
-          </tbody>
-        </table>
-      </div>
-    </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):
- * Thomas Junk <thomas.junk@intevation.de>
- */
-
-import { HTTP } from "@/lib/http.js";
-import { displayError } from "@/lib/errors.js";
-import locale2 from "locale2";
-
-export default {
-  name: "importqueuedetail",
-  props: ["job", "reload"],
-  data() {
-    return {
-      loading: false,
-      show: false,
-      entries: [],
-      sortAsc: true
-    };
-  },
-  mounted() {
-    this.openSpecificDetail();
-  },
-  watch: {
-    $route() {
-      this.openSpecificDetail();
-    },
-    reload() {
-      if (this.reload) {
-        this.entries = [];
-        this.show = false;
-      }
-    }
-  },
-  methods: {
-    openSpecificDetail() {
-      const { id } = this.$route.params;
-      if (id == this.job.id) {
-        this.showDetails(id);
-      } else {
-        this.show = false;
-      }
-    },
-    formatDate(date) {
-      return date
-        ? new Date(date).toLocaleDateString(locale2, {
-            day: "2-digit",
-            month: "2-digit",
-            year: "numeric"
-          })
-        : "";
-    },
-    formatDateTime(date) {
-      if (!date) return "";
-      const d = new Date(date);
-      return (
-        d.toLocaleDateString(locale2, {
-          day: "2-digit",
-          month: "2-digit",
-          year: "numeric"
-        }) +
-        " - " +
-        d.toLocaleTimeString(locale2, {
-          hour12: false
-        })
-      );
-    },
-    showDetails(id) {
-      if (this.show) {
-        this.show = false;
-        return;
-      }
-      if (this.entries.length === 0) {
-        this.loading = true;
-        HTTP.get("/imports/" + id, {
-          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
-        })
-          .then(response => {
-            const { entries } = response.data;
-            this.entries = entries;
-            this.show = true;
-            this.loading = false;
-          })
-          .catch(error => {
-            const { status, data } = error.response;
-            displayError({
-              title: this.$gettext("Backend Error"),
-              message: `${status}: ${data.message || data}`
-            });
-          });
-      } else {
-        this.show = true;
-      }
-    }
-  },
-  computed: {
-    sortedEntries() {
-      let sorted = this.entries.slice();
-      sorted.sort((r1, r2) => {
-        let d1 = new Date(r1.time);
-        let d2 = new Date(r2.time);
-        if (d2 < d1) {
-          return !this.sortAsc ? -1 : 1;
-        }
-        if (d2 > d1) {
-          return !this.sortAsc ? 1 : -1;
-        }
-        return 0;
-      });
-      return sorted;
-    },
-    sortIcon() {
-      return this.sortAsc ? "sort-amount-down" : "sort-amount-up";
-    },
-    icon() {
-      return {
-        "angle-up": !this.show,
-        "angle-down": this.show
-      };
-    },
-    collapse() {
-      return {
-        details: true,
-        collapse: true,
-        show: this.show,
-        "w-100": true
-      };
-    }
-  }
-};
-</script>
-
-<style lang="scss" scoped>
-.condensed {
-  font-stretch: condensed;
-}
-
-.entry {
-  background-color: white;
-  cursor: pointer;
-  width: 100%;
-}
-
-.entry:hover {
-  background-color: #efefef;
-  transition: 1.6s;
-}
-
-.detailstable {
-  margin-left: $offset;
-  margin-right: $large-offset;
-}
-
-.detailsbutton {
-  position: absolute;
-  top: 0;
-  right: 0;
-  height: 100%;
-}
-.jobid {
-  width: 15%;
-}
-
-.enqueued {
-  width: 15%;
-}
-
-.user {
-  width: 15%;
-}
-
-.signer {
-  width: 15%;
-}
-
-.kind {
-  width: 10%;
-}
-
-.state {
-  width: 15%;
-}
-
-.details {
-  width: 50%;
-}
-
-.detailsrow {
-  line-height: 0.1em;
-}
-
-.type {
-  width: 65px;
-  white-space: nowrap;
-  padding-left: 0px;
-  border-top: 0px;
-  padding-bottom: $small-offset;
-}
-
-.datetime {
-  width: 200px;
-  white-space: nowrap;
-  padding-left: 0px;
-  border-top: 0px;
-  padding-bottom: $small-offset;
-}
-
-.message {
-  min-width: 700px;
-  white-space: nowrap;
-  padding-left: 0px;
-  border-top: 0px;
-  padding-bottom: $small-offset;
-}
-
-thead,
-tbody {
-  display: block;
-}
-
-tbody {
-  height: 150px;
-  overflow-y: auto;
-  overflow-x: auto;
-}
-</style>
--- a/client/src/components/staging/Staging.vue	Thu Feb 28 13:46:09 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,193 +0,0 @@
-<template>
-  <div class="w-90 stagingcard">
-    <UIBoxHeader
-      icon="clipboard-check"
-      title="Staging Area"
-      :closeCallback="$parent.close"
-    />
-    <div class="mt-3 pl-3 pr-3">
-      <div class="mt-3 text-left flex-row d-flex border-bottom">
-        <div class="header text-left name"><translate>Name</translate></div>
-        <div class="header text-left type"><translate>Type</translate></div>
-        <div class="header text-left date"><translate>Date</translate></div>
-        <div class="header text-left imported">
-          <translate>Imported</translate>
-        </div>
-        <div class="header text-left username">
-          <translate>Username</translate>
-        </div>
-        <div class="ml-3 controls"></div>
-      </div>
-      <div class="mt-3 stagingdetails details" v-if="filteredData.length > 0">
-        <StagingDetail
-          class="mb-3 border-bottom"
-          :key="data.id"
-          v-for="data in filteredData"
-          :data="data"
-        ></StagingDetail>
-      </div>
-    </div>
-    <div class="mt-3 p-3" v-if="filteredData.length > 0">
-      <button @click="confirmReview" class="confirm-button btn btn-info">
-        <translate>Confirm</translate>
-      </button>
-    </div>
-    <div v-else class="mr-auto ml-auto"><translate>No results.</translate></div>
-    <div class="mt-1 p-3">
-      <button @click="loadData" class="refresh btn btn-dark">Refresh</button>
-    </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):
- * Thomas Junk <thomas.junk@intevation.de>
- * Markus Kottländer <markus@intevation.de>
- */
-import { mapState } from "vuex";
-import { HTTP } from "@/lib/http.js";
-import { displayError, displayInfo } from "@/lib/errors.js";
-import { STATES } from "@/store/imports.js";
-
-export default {
-  data() {
-    return {};
-  },
-  components: {
-    StagingDetail: () => import("./StagingDetail")
-  },
-  mounted() {
-    this.loadData();
-  },
-  computed: {
-    ...mapState("application", ["searchQuery"]),
-    ...mapState("imports", ["staging"]),
-    filteredData() {
-      return this.staging.filter(data => {
-        const result = [data.id + "", data.enqueued, data.kind, data.user].some(
-          x => x.toLowerCase().includes(this.searchQuery.toLowerCase())
-        );
-        return result;
-      });
-    }
-  },
-  methods: {
-    loadData() {
-      this.$store.dispatch("imports/getStaging").catch(error => {
-        const { status, data } = error.response;
-        displayError({
-          title: "Backend Error",
-          message: `${status}: ${data.message || data}`
-        });
-      });
-    },
-    confirmReview() {
-      const reviewResults = this.staging
-        .filter(x => x.status !== STATES.NEEDSAPPROVAL)
-        .map(r => {
-          return {
-            id: r.id,
-            state: r.status
-          };
-        });
-      if (!reviewResults.length) return;
-      HTTP.patch("/imports", reviewResults, {
-        headers: {
-          "X-Gemma-Auth": localStorage.getItem("token"),
-          "Content-type": "application/json"
-        }
-      })
-        .then(response => {
-          const messages = response.data
-            .map(x => {
-              if (x.message) return x.message;
-              if (x.error) return x.error;
-            })
-            .join("\n\n");
-          displayInfo({
-            title: "Staging Area",
-            message: messages,
-            options: {
-              timeout: 0,
-              buttons: [{ text: "Ok", action: null, bold: true }]
-            }
-          });
-          this.loadData();
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: "Backend Error",
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    }
-  },
-  STATES: STATES
-};
-</script>
-
-<style lang="scss" scoped>
-.stagingdetails {
-  overflow-y: auto;
-  max-height: 250px;
-}
-.name {
-  width: 180px;
-}
-
-.date {
-  width: 90px;
-}
-
-.type {
-  width: 40px;
-}
-
-.imported {
-  width: 90px;
-}
-
-.username {
-  width: 150px;
-}
-
-.controls {
-  width: 60px;
-}
-
-.refresh {
-  position: absolute;
-  left: $offset;
-  bottom: $offset;
-}
-.table th,
-td {
-  font-size: 0.9rem;
-  border-top: 0px !important;
-  border-bottom-width: 1px;
-  text-align: left;
-  padding: 0.5rem !important;
-}
-
-.stagingcard {
-  position: relative;
-  min-height: 150px;
-}
-
-.confirm-button {
-  position: absolute;
-  right: $offset;
-  bottom: $offset;
-}
-</style>
--- a/client/src/components/staging/StagingDetail.vue	Thu Feb 28 13:46:09 2019 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,583 +0,0 @@
-<template>
-  <div :class="detail">
-    <div class="d-flex flex-row">
-      <div class="mt-auto d-flex flex-row mb-auto small name text-left">
-        <a
-          v-if="isSoundingResult(data.kind.toUpperCase())"
-          class="text-left"
-          @click="zoomTo()"
-          href="#"
-          >{{ data.summary.bottleneck }}</a
-        >
-        <span v-if="isBottleneck(data.kind.toUpperCase())" class="text-left"
-          ><translate>Bottlenecks</translate> ({{
-            data.summary.bottlenecks.length
-          }})</span
-        >
-        <a
-          v-if="isApprovedGaugeMeasurement(data.kind.toUpperCase())"
-          class="text-left"
-          ><translate>Approved Gauge Measurements</translate> ({{
-            data.summary.length
-          }})</a
-        >
-        <span
-          class="text-left"
-          v-if="isFairwayDimension(data.kind.toUpperCase())"
-          >{{ data.summary["source-organization"] }} (LOS:
-          {{ data.summary.los }})</span
-        >
-        <a
-          href="#"
-          class="text-left"
-          @click="zoomToStretch(data.summary.stretch)"
-          v-if="isStretch(data.kind.toUpperCase())"
-          >{{ data.summary.stretch }}</a
-        >
-      </div>
-      <div class="mt-auto mb-auto small text-left type">
-        {{ data.kind.toUpperCase() }}
-      </div>
-      <div v-if="data.summary" class="mt-auto mb-auto small text-left date">
-        {{ formatSurveyDate(data.summary.date) }}
-      </div>
-      <div v-else class="mt-auto mb-auto small text-left date">-</div>
-      <div class="mt-auto mb-auto small text-left imported">
-        {{ formatSurveyDate(data.enqueued.split("T")[0]) }}
-      </div>
-      <div class="mt-auto mb-auto small text-left username">
-        {{ data.user }}
-      </div>
-      <div class="controls d-flex flex-row justify-content-end">
-        <div>
-          <button
-            :class="{
-              'ml-3': true,
-              'mr-3': true,
-              btn: true,
-              'btn-sm': true,
-              'btn-outline-success': needsApproval(data) || isRejected(data),
-              'btn-success': isApproved(data)
-            }"
-            @click="toggleApproval(data.id, $options.STATES.APPROVED)"
-          >
-            <font-awesome-icon icon="check"></font-awesome-icon>
-          </button>
-        </div>
-        <div>
-          <button
-            :class="{
-              'mr-3': true,
-              btn: true,
-              'btn-sm': true,
-              'btn-outline-danger': needsApproval(data) || isApproved(data),
-              'btn-danger': isRejected(data)
-            }"
-            @click="toggleApproval(data.id, $options.STATES.REJECTED)"
-          >
-            <font-awesome-icon icon="times" class="pointer"></font-awesome-icon>
-          </button>
-        </div>
-        <div
-          v-if="
-            !isBottleneck(data.kind.toUpperCase()) ||
-              isApprovedGaugeMeasurement(data.kind.toUpperCase())
-          "
-          class="expander"
-        ></div>
-        <div v-if="isBottleneck(data.kind.toUpperCase())">
-          <div class="mt-auto mb-auto text-info text-left">
-            <font-awesome-icon
-              class="pointer"
-              @click="showDetails()"
-              v-if="show"
-              icon="angle-up"
-              fixed-width
-            ></font-awesome-icon>
-            <font-awesome-icon
-              class="pointer"
-              @click="showDetails()"
-              v-if="loading"
-              icon="spinner"
-              fixed-width
-            ></font-awesome-icon>
-            <font-awesome-icon
-              @click="showDetails()"
-              class="pointer"
-              v-if="!show && !loading"
-              icon="angle-down"
-              fixed-width
-            ></font-awesome-icon>
-          </div>
-        </div>
-        <div v-if="isApprovedGaugeMeasurement(data.kind.toUpperCase())">
-          <div
-            @click="showAGMDetails = !showAGMDetails"
-            class="mt-auto mb-auto text-info text-left"
-          >
-            <font-awesome-icon
-              class="pointer"
-              v-if="showAGMDetails"
-              icon="angle-up"
-              fixed-width
-            ></font-awesome-icon>
-            <font-awesome-icon
-              class="pointer"
-              v-if="!showAGMDetails"
-              icon="angle-down"
-              fixed-width
-            ></font-awesome-icon>
-          </div>
-        </div>
-        <div v-else class="empty"></div>
-      </div>
-    </div>
-    <div v-if="show && bottlenecks.length > 0" class="bottlenecksdetails">
-      <div
-        v-for="(bottleneck, index) in bottlenecks"
-        :key="index"
-        class="d-flex flex-row"
-      >
-        <div class="d-flex flex-column">
-          <div class="d-flex flex-row">
-            <a @click="moveToBottleneck(index)" class="small" href="#">{{
-              bottleneck.properties.objnam
-            }}</a>
-            <div
-              @click="showBottleneckDetails(index)"
-              class="small mt-auto mb-auto text-info text-left"
-            >
-              <font-awesome-icon
-                class="pointer"
-                v-if="showBottleneckDetail === index"
-                icon="angle-up"
-                fixed-width
-              ></font-awesome-icon>
-              <font-awesome-icon
-                class="pointer"
-                v-if="!(showBottleneckDetail === index)"
-                icon="angle-down"
-                fixed-width
-              ></font-awesome-icon>
-            </div>
-          </div>
-
-          <div class="d-flex flex-row" v-if="showBottleneckDetail === index">
-            <table>
-              <tr
-                v-for="(info, index) in Object.keys(bottleneck.properties)"
-                :key="index"
-                class="mr-1 small text-muted"
-              >
-                <td class="condensed text-left">{{ info }}</td>
-                <td class="condensed pl-3 text-left">
-                  {{ bottleneck.properties[info] }}
-                </td>
-              </tr>
-            </table>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div v-if="showAGMDetails">
-      <div class="pl-3 d-flex flex-row">
-        <span class="condensed agmcode text-left"
-          ><small><translate>ISRS Code</translate></small></span
-        >
-        <span class="condensed agmdetail text-left"
-          ><small><translate>Date of measurement</translate></small></span
-        >
-      </div>
-      <div class="diffs">
-        <div v-for="(result, index) in data.summary" :key="index">
-          <div class="pl-3 d-flex flex-row">
-            <span
-              v-if="result.versions.length == 1"
-              class="condensed agmcode text-left"
-              ><small
-                >{{ result["fk-gauge-id"] }}
-                <translate>( New )</translate></small
-              ></span
-            >
-            <span
-              v-if="result.versions.length == 2"
-              class="condensed agmcode text-left"
-              ><small>{{ result["fk-gauge-id"] }}</small></span
-            >
-            <span class="condensed agmdetail text-left"
-              ><small>{{ formatDateTime(result["measure-date"]) }}</small></span
-            >
-            <div
-              @click="toggleDiff(index)"
-              class="small ml-auto mt-auto mb-auto text-info text-left"
-            >
-              <font-awesome-icon
-                class="pointer"
-                v-if="showDiff == index"
-                icon="angle-up"
-                fixed-width
-              ></font-awesome-icon>
-              <font-awesome-icon
-                class="pointer"
-                v-if="showDiff != index"
-                icon="angle-down"
-                fixed-width
-              ></font-awesome-icon>
-            </div>
-          </div>
-          <div v-if="showDiff == index" class="pl-3 d-flex flex-row">
-            <div>
-              <div class="d-flex flex-row condensed pl-3 text-left">
-                <div class="header border-bottom agmdetailskeys">
-                  <small><translate>Value</translate></small>
-                </div>
-                <div
-                  v-if="result.versions.length == 2"
-                  class="header border-bottom agmdetailsvalues"
-                >
-                  <small><translate>Old</translate></small>
-                </div>
-                <div class="header border-bottom agmdetailsvalues">
-                  <small><translate>New</translate></small>
-                </div>
-              </div>
-              <div
-                class="d-flex flex-row condensed pl-3 text-left"
-                v-for="(entry, index) in Object.keys(result.versions[0])"
-                :key="index"
-              >
-                <div
-                  v-if="
-                    result.versions.length == 1 ||
-                      result.versions[0][entry] != result.versions[1][entry]
-                  "
-                  class="agmdetailskeys"
-                >
-                  <small>{{ entry }}</small>
-                </div>
-                <div
-                  v-if="
-                    result.versions.length == 1 ||
-                      result.versions[0][entry] != result.versions[1][entry]
-                  "
-                  class="agmdetailsvalues"
-                >
-                  <small>{{ result.versions[0][entry] }}</small>
-                </div>
-                <div
-                  v-if="
-                    result.versions.length == 2 &&
-                      result.versions[0][entry] != result.versions[1][entry]
-                  "
-                  class="agmdetailsvalues"
-                >
-                  <small>{{ result.versions[1][entry] }}</small>
-                </div>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </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):
- * Thomas Junk <thomas.junk@intevation.de>
- */
-
-import { formatSurveyDate, formatDateTime } from "@/lib/date.js";
-import { STATES } from "@/store/imports.js";
-import { HTTP } from "@/lib/http";
-import { WFS } from "ol/format.js";
-import { or as orFilter, equalTo as equalToFilter } from "ol/format/filter.js";
-import { displayError } from "@/lib/errors.js";
-import { mapState } from "vuex";
-import { LAYERS } from "@/store/map.js";
-
-const NO_DIFF = -1;
-const NO_BOTTLENECK = -1;
-
-export default {
-  name: "stagingdetail",
-  props: ["data"],
-  data() {
-    return {
-      showDiff: NO_DIFF,
-      showAGMDetails: false,
-      showBottleneckDetail: NO_BOTTLENECK,
-      show: false,
-      loading: false,
-      bottlenecks: []
-    };
-  },
-  mounted() {
-    this.bottlenecks = [];
-    const { id } = this.$route.params;
-    this.$store.commit("imports/setImportToReview", id);
-    if (this.open) this.showDetails();
-  },
-  computed: {
-    ...mapState("imports", ["importToReview"]),
-    open() {
-      return this.importToReview == this.data.id;
-    },
-    detail() {
-      return [
-        "pb-2",
-        "pt-2",
-        "d-flex",
-        "flex-column",
-        "w-100",
-        {
-          highlight: this.open && this.needsApproval(this.data)
-        }
-      ];
-    }
-  },
-  watch: {
-    showAGMDetails() {
-      if (!this.showAGMDetails) this.showDiff = NO_DIFF;
-    },
-    open() {
-      this.show = this.open;
-    },
-    $route() {
-      const { id } = this.$route.params;
-      this.$store.commit("imports/setImportToReview", id);
-      if (this.open) this.showDetails();
-    }
-  },
-  methods: {
-    showBottleneckDetails(index) {
-      if (index == this.showBottleneckDetail) {
-        this.showBottleneckDetail = NO_BOTTLENECK;
-        return;
-      }
-      this.showBottleneckDetail = index;
-    },
-    toggleDiff(number) {
-      if (this.showDiff !== number || this.showDiff == -1) {
-        this.showDiff = number;
-      } else {
-        this.showDiff = -1;
-      }
-    },
-    zoomToStretch(name) {
-      this.$store.commit("map/setLayerVisible", LAYERS.STRETCHES);
-      this.$store
-        .dispatch("imports/loadStretch", name)
-        .then(response => {
-          if (response.data.features.length < 1)
-            throw new Error("no feaures found for: " + name);
-          this.moveToExtent(response.data.features[0]);
-        })
-        .catch(error => {
-          console.log(error);
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    },
-    showDetails() {
-      if (!this.isBottleneck(this.data.kind.toUpperCase())) return;
-      if (this.show) {
-        this.show = false;
-        return;
-      }
-      if (this.bottlenecks.length > 0) {
-        this.show = true;
-        return;
-      }
-      this.loading = true;
-      const generateFilter = () => {
-        const { bottlenecks } = this.data.summary;
-        if (bottlenecks.length === 1)
-          return equalToFilter("bottleneck_id", bottlenecks[0]);
-        const orExpressions = bottlenecks.map(x => {
-          return equalToFilter("bottleneck_id", x);
-        });
-        return orFilter(...orExpressions);
-      };
-      const filterExpression = generateFilter();
-      const bottleneckFeatureCollectionRequest = new WFS().writeGetFeature({
-        srsName: "EPSG:4326",
-        featureNS: "gemma",
-        featurePrefix: "gemma",
-        featureTypes: ["bottlenecks_geoserver"],
-        outputFormat: "application/json",
-        filter: filterExpression
-      });
-      HTTP.post(
-        "/internal/wfs",
-        new XMLSerializer().serializeToString(
-          bottleneckFeatureCollectionRequest
-        ),
-        {
-          headers: {
-            "X-Gemma-Auth": localStorage.getItem("token"),
-            "Content-type": "text/xml; charset=UTF-8"
-          }
-        }
-      )
-        .then(response => {
-          this.bottlenecks = response.data.features;
-          this.show = true;
-          this.loading = false;
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    },
-    isFairwayDimension(kind) {
-      return kind === "FD";
-    },
-    isApprovedGaugeMeasurement(kind) {
-      return kind === "AGM";
-    },
-    isBottleneck(kind) {
-      return kind === "BN" || kind === "UBN";
-    },
-    isStretch(kind) {
-      return kind === "ST";
-    },
-    isSoundingResult(kind) {
-      return kind === "SR";
-    },
-    formatSurveyDate(date) {
-      return formatSurveyDate(date);
-    },
-    formatDateTime(date) {
-      return formatDateTime(date);
-    },
-    needsApproval(item) {
-      return item.status === STATES.NEEDSAPPROVAL;
-    },
-    isRejected(item) {
-      return item.status === STATES.REJECTED;
-    },
-    isApproved(item) {
-      return item.status === STATES.APPROVED;
-    },
-    moveToBottleneck(index) {
-      this.$store.commit("map/setLayerVisible", LAYERS.BOTTLENECKS);
-      this.moveToExtent(this.bottlenecks[index]);
-    },
-    moveToExtent(feature) {
-      this.$store.commit("map/moveToExtent", {
-        feature: feature,
-        zoom: 17,
-        preventZoomOut: true
-      });
-    },
-    moveMap(coordinates) {
-      this.$store.commit("map/moveMap", {
-        coordinates: coordinates,
-        zoom: 17,
-        preventZoomOut: true
-      });
-    },
-    zoomTo() {
-      const { lat, lon, bottleneck, date } = this.data.summary;
-      const coordinates = [lat, lon];
-      this.moveMap(coordinates);
-      this.$store
-        .dispatch("bottlenecks/setSelectedBottleneck", bottleneck)
-        .then(() => {
-          this.$store.commit("bottlenecks/setSelectedSurveyByDate", date);
-        });
-    },
-    toggleApproval(id, newStatus) {
-      this.$store.commit("imports/toggleApproval", {
-        id: id,
-        newStatus: newStatus
-      });
-    }
-  },
-  STATES: STATES
-};
-</script>
-
-<style lang="scss" scoped>
-.expander {
-  margin-right: 1.25rem;
-}
-
-.bottlenecksdetails {
-  overflow-y: auto;
-  max-height: 250px;
-}
-.diffs {
-  overflow-y: auto;
-  max-height: 250px;
-}
-
-.highlight {
-  background-color: #f9f9f9;
-}
-
-.condensed {
-  font-stretch: condensed;
-}
-
-.empty {
-  margin-right: 20px;
-}
-
-.name {
-  width: 180px;
-}
-
-.date {
-  width: 90px;
-}
-
-.type {
-  width: 40px;
-}
-
-.imported {
-  width: 90px;
-}
-
-.username {
-  width: 150px;
-}
-
-.controls {
-  width: 60px;
-}
-
-.agmcode {
-  width: 200px;
-}
-
-.agmdate {
-  width: 100px;
-}
-
-.agmdetailskeys {
-  width: 130px;
-}
-
-.agmdetailsvalues {
-  width: 200px;
-}
-</style>