changeset 3263:d23532a4d0c3

client: define stretches: renamed component file and moved to subdirectory as preparation for splitting list and edit view into separate components and then duplicating for sections
author Markus Kottlaender <markus@intevation.de>
date Wed, 15 May 2019 12:04:14 +0200
parents a2e9671f4ad6
children 9ae43313b463
files client/src/components/Contextbox.vue client/src/components/ImportStretches.vue client/src/components/stretches/Stretches.vue
diffstat 3 files changed, 606 insertions(+), 606 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/Contextbox.vue	Wed May 15 11:47:56 2019 +0200
+++ b/client/src/components/Contextbox.vue	Wed May 15 12:04:14 2019 +0200
@@ -28,7 +28,7 @@
   name: "contextbox",
   components: {
     Bottlenecks: () => import("@/components/Bottlenecks"),
-    Stretches: () => import("@/components/ImportStretches"),
+    Stretches: () => import("@/components/stretches/Stretches"),
     ImportOverview: () => import("@/components/importoverview/ImportOverview"),
     ImportConfiguration: () => import("@/components/importconfiguration/Import")
   },
--- a/client/src/components/ImportStretches.vue	Wed May 15 11:47:56 2019 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,605 +0,0 @@
-<template>
-  <div class="d-flex flex-column mb-3">
-    <UIBoxHeader
-      icon="road"
-      :title="defineStretchesLabel"
-      :closeCallback="$parent.close"
-    />
-    <div class="position-relative">
-      <UISpinnerOverlay v-if="loading" />
-      <div v-if="!edit" class="mb-3">
-        <UITableHeader
-          :columns="[
-            { id: 'properties.name', title: `${nameLabel}`, class: 'col-4' },
-            {
-              id: 'properties.date_info',
-              title: `${dateLabel}`,
-              class: 'col-2'
-            },
-            {
-              id: 'properties.source_organization',
-              title: `${sourceorganizationLabel}`,
-              class: 'col-3'
-            }
-          ]"
-        />
-        <UITableBody
-          :data="filteredStretches() | sortTable(sortColumn, sortDirection)"
-        >
-          <template v-slot:row="{ item: stretch }">
-            <div class="py-1 col-4 ">
-              <a
-                class="pointer text-info"
-                v-if="isInStaging(stretch.properties.name)"
-                @click="gotoStaging(getStagingLink(stretch.properties.name))"
-              >
-                {{ stretch.properties.name
-                }}<font-awesome-icon
-                  class="ml-1 text-danger"
-                  icon="exclamation-triangle"
-                  fixed-width
-                ></font-awesome-icon
-                ><small class="ml-1">review</small>
-              </a>
-              <a v-else @click="moveMapToStretch(stretch)" href="#">{{
-                stretch.properties.name
-              }}</a>
-            </div>
-            <div class="py-1 col-2">
-              {{ stretch.properties.date_info | surveyDate }}
-            </div>
-            <div class="py-1 col-3">
-              {{ stretch.properties.source_organization }}
-            </div>
-            <div class="py-1 col text-right">
-              <button
-                class="btn btn-xs btn-dark mr-1"
-                @click="editStretch(stretch)"
-              >
-                <font-awesome-icon icon="pencil-alt" fixed-width />
-              </button>
-              <button
-                class="btn btn-xs btn-dark"
-                @click="deleteStretch(stretch)"
-              >
-                <font-awesome-icon icon="trash" fixed-width />
-              </button>
-            </div>
-          </template>
-        </UITableBody>
-      </div>
-      <div v-if="edit">
-        <div class="ml-3 mr-3">
-          <div class="d-flex flex-row justify-content-between">
-            <div class="mt-2 w-50 mr-2 text-left">
-              <small class="text-muted"> <translate>ID</translate> </small>
-              <input
-                id="id"
-                type="text"
-                class="form-control"
-                placeholder="AT_Section_12"
-                aria-label="id"
-                v-model="id"
-                :disabled="editExistingStretch"
-              />
-              <span class="text-left text-danger">
-                <small v-if="idError && !id">
-                  <translate>Please enter an id</translate>
-                </small>
-              </span>
-            </div>
-            <div class="mt-2 w-50 ml-2 text-left">
-              <div>
-                <small class="text-muted">
-                  <translate>Countrycode</translate>
-                </small>
-                <input
-                  id="countryCode"
-                  type="text"
-                  class="form-control"
-                  placeholder="AT"
-                  aria-label="id"
-                  v-model="countryCode"
-                />
-                <span class="text-left text-danger">
-                  <small v-if="countryCodeError && !countryCode">
-                    <translate>Please enter a countrycode </translate>
-                  </small>
-                </span>
-              </div>
-              <div class="w-50 ml-2"></div>
-            </div>
-          </div>
-          <div class="d-flex flex-column  justify-content-between">
-            <div class="mt-2 text-left">
-              <small class="text-muted">
-                <translate>Start rhm</translate>
-              </small>
-              <div class="d-flex flex-row">
-                <input
-                  id="startrhm"
-                  type="text"
-                  class="form-control"
-                  placeholder="e.g. ATXXX000010000019900"
-                  aria-label="startrhm"
-                  v-model="startrhm"
-                />
-                <span class="input-group-text" @click="togglePipette('start')">
-                  <font-awesome-icon
-                    :class="{ 'text-info': pipetteStart }"
-                    icon="crosshairs"
-                  />
-                </span>
-              </div>
-              <span class="text-left text-danger">
-                <small v-if="startrhmError && !startrhm">
-                  <translate>Please enter a start point</translate>
-                </small>
-              </span>
-            </div>
-            <div class="mt-2 text-left">
-              <small class="text-muted"> <translate>End rhm</translate> </small>
-              <div class="d-flex flex-row">
-                <input
-                  id="endrhm"
-                  type="text"
-                  class="form-control"
-                  placeholder="e.g. ATXXX000010000019900"
-                  aria-label="endrhm"
-                  v-model="endrhm"
-                />
-                <span class="input-group-text" @click="togglePipette('end')">
-                  <font-awesome-icon
-                    :class="{ 'text-info': pipetteEnd }"
-                    icon="crosshairs"
-                  />
-                </span>
-              </div>
-              <span class="text-left text-danger">
-                <small v-if="endrhmError && !endrhm">
-                  <translate>Please enter an end point</translate>
-                </small>
-              </span>
-            </div>
-          </div>
-          <div
-            v-if="!editExistingStretch"
-            class="d-flex flex-row justify-content-between"
-          >
-            <div class="mt-2  mr-2 w-50  text-left">
-              <small class="text-muted">
-                <translate
-                  >Tolerance for snapping of waterway axis [m]</translate
-                >
-              </small>
-              <input
-                class="form-control"
-                v-model.number="tolerance"
-                placeholder=""
-                type="number"
-                min="0"
-                step="any"
-                aria-label="tolerance"
-                id="tolerance"
-              />
-              <span class="text-left text-danger">
-                <small v-if="toleranceError && !tolerance">
-                  <translate>Please enter a tolerance value</translate>
-                </small>
-              </span>
-            </div>
-          </div>
-          <div class="d-flex flex-row justify-content-between">
-            <div class="mt-2  mr-2 w-50  text-left">
-              <small class="text-muted">
-                <translate>Object name</translate>
-              </small>
-              <input
-                id="objbn"
-                type="text"
-                class="form-control"
-                placeholder=""
-                aria-label="objbn"
-                v-model="objbn"
-              />
-              <span class="text-left text-danger">
-                <small v-if="objbnError && !objbn">
-                  <translate>Please enter an objectname</translate>
-                </small>
-              </span>
-            </div>
-            <div class="mt-2  ml-2 w-50  text-left">
-              <small class="text-muted">
-                <translate>National Object name</translate>
-              </small>
-              <input
-                id="nobjbn"
-                type="text"
-                class="form-control"
-                placeholder=""
-                aria-label="nobjbn"
-                v-model="nobjbn"
-              />
-            </div>
-          </div>
-          <div class="d-flex flex-row justify-content-between">
-            <div class="mt-2 mr-2 w-50 text-left">
-              <small class="text-muted">
-                <translate>Date info</translate>
-              </small>
-              <input
-                id="date_info"
-                type="date"
-                class="form-control"
-                placeholder="date_info"
-                aria-label="date_info"
-                v-model="date_info"
-              />
-              <span class="text-left text-danger">
-                <small v-if="date_infoError && !date_info">
-                  <translate>Please enter a date</translate>
-                </small>
-              </span>
-            </div>
-            <div class="mt-2 ml-2 w-50 text-left">
-              <small class="text-muted"> <translate>Source</translate> </small>
-              <input
-                id="source"
-                type="text"
-                class="form-control"
-                placeholder="source"
-                aria-label="source"
-                v-model="source"
-              />
-              <span class="text-left text-danger">
-                <small v-if="sourceError && !source">
-                  <translate>Please enter a source</translate>
-                </small>
-              </span>
-            </div>
-          </div>
-        </div>
-        <div class="text-right mt-2 mr-3 mb-3">
-          <button @click="edit = false" class="btn btn-warning mr-2">
-            Back
-          </button>
-          <button
-            @click="save"
-            type="submit"
-            class="shadow-sm btn btn-info submit-button"
-          >
-            <translate>Submit</translate>
-          </button>
-        </div>
-      </div>
-      <div class="text-right mr-3">
-        <button v-if="!edit" @click="startEdit()" class="btn btn-info">
-          <translate>New stretch</translate>
-        </button>
-      </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, 2019 by via donau
- *   – Österreichische Wasserstraßen-Gesellschaft mbH
- * Software engineering by Intevation GmbH
- *
- * Author(s):
- * Thomas Junk <thomas.junk@intevation.de>
- * Tom Gottfried <tom.gottfried@intevation.de>
- */
-import { mapState, mapGetters } from "vuex";
-import { displayError, displayInfo } from "@/lib/errors";
-import { HTTP } from "@/lib/http";
-import { sortTable } from "@/lib/mixins";
-
-export default {
-  name: "importstretches",
-  mixins: [sortTable],
-  data() {
-    return {
-      staging: [],
-      edit: false,
-      editExistingStretch: false,
-      id: "",
-      funktion: "",
-      startrhm: "",
-      endrhm: "",
-      tolerance: 5,
-      objbn: "",
-      nobjbn: "",
-      countryCode: "",
-      date_info: new Date().toISOString().split("T")[0],
-      source: "",
-      pipetteStart: false,
-      pipetteEnd: false,
-      idError: false,
-      funktionError: false,
-      startrhmError: false,
-      endrhmError: false,
-      toleranceError: false,
-      objbnError: false,
-      nobjbnError: false,
-      date_infoError: false,
-      sourceError: false,
-      countryCodeError: false,
-      loading: false
-    };
-  },
-  computed: {
-    ...mapState("application", ["searchQuery"]),
-    ...mapState("map", ["identifiedFeatures", "currentMeasurement"]),
-    ...mapGetters("map", ["openLayersMap"]),
-    ...mapGetters("user", ["isSysAdmin"]),
-    ...mapState("imports", ["stretches"]),
-    defineStretchesLabel() {
-      return this.$gettext("Define Stretches");
-    },
-    nameLabel() {
-      return this.$gettext("Name");
-    },
-    dateLabel() {
-      return this.$gettext("Date");
-    },
-    sourceorganizationLabel() {
-      return this.$gettext("Source organization");
-    },
-    stretchesInStaging() {
-      const result = [];
-      for (let stretch of this.stretches) {
-        for (let s of this.staging) {
-          if (s.kind == "st" && s.summary.stretch == stretch.properties.name) {
-            result.push({ name: s.summary.stretch, id: s.id });
-          }
-        }
-      }
-      return result;
-    }
-  },
-  watch: {
-    identifiedFeatures() {
-      const distanceMark = this.identifiedFeatures.find(x =>
-        /^distance_marks_geoserver/.test(x["id_"])
-      );
-      if (distanceMark) {
-        const location = distanceMark.get("location");
-        this.startrhm = this.pipetteStart ? location : this.startrhm;
-        this.endrhm = this.pipetteEnd ? location : this.endrhm;
-        this.pipetteStart = false;
-        this.pipetteEnd = false;
-        this.$store.commit("map/mapPopupEnabled", true);
-      }
-    }
-  },
-  methods: {
-    filteredStretches() {
-      return this.stretches.filter(s => {
-        return (s.properties.name + s.properties.source_organization)
-          .toLowerCase()
-          .includes(this.searchQuery.toLowerCase());
-      });
-    },
-    gotoStaging(id) {
-      this.$router.push("/review/" + id);
-    },
-    isInStaging(stretchname) {
-      for (let s of this.stretchesInStaging) {
-        if (s.name == stretchname) return true;
-      }
-      return false;
-    },
-    getStagingLink(stretchname) {
-      for (let s of this.stretchesInStaging) {
-        if (s.name == stretchname) return s.id;
-      }
-    },
-    loadStagingData() {
-      return new Promise((resolve, reject) => {
-        HTTP.get("/imports?states=pending", {
-          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
-        })
-          .then(response => {
-            const { imports } = response.data;
-            this.staging = imports;
-            resolve(response);
-          })
-          .catch(error => {
-            reject(error);
-          });
-      });
-    },
-    editStretch(stretch) {
-      const properties = stretch.properties;
-      this.date_info = properties.date_info.split("T")[0];
-      this.id = properties.name;
-      this.nobjbn = properties.nobjnam;
-      this.objbn = properties.objnam;
-      this.countryCode = properties.countries;
-      this.source = properties["source_organization"];
-      this.edit = true;
-      this.startrhm = properties.lower;
-      this.endrhm = properties.upper;
-      this.editExistingStretch = true;
-    },
-    deleteStretch(stretch) {
-      this.$store.commit("application/popup", {
-        icon: "trash",
-        title: this.$gettext("Delete Stretch"),
-        content:
-          this.$gettext("Do you really want to delete this stretch:") +
-          `<br>
-        <b>${stretch.properties.name}, ${
-            stretch.properties.source_organization
-          } (${stretch.properties.countries})</b>`,
-        confirm: {
-          label: this.$gettext("Delete"),
-          icon: "trash",
-          callback: () => {
-            displayInfo({
-              title: this.$gettext("Not implemented"),
-              message: this.$gettext("Deleting ") + stretch.id
-            });
-          }
-        },
-        cancel: {
-          label: this.$gettext("Cancel"),
-          icon: "times"
-        }
-      });
-    },
-    moveMapToStretch(stretch) {
-      this.$store.commit("imports/selectedStretchId", stretch.id);
-      this.openLayersMap()
-        .getLayer("STRETCHES")
-        .setVisible(true);
-      this.$store.dispatch("map/moveToFeauture", {
-        feature: stretch,
-        zoom: 17,
-        preventZoomOut: true
-      });
-    },
-    clean() {
-      this.id = "";
-      this.edit = false;
-      this.editExistingStretch = false;
-      this.funktion = "";
-      this.startrhm = "";
-      this.tolerance = 5;
-      this.endrhm = "";
-      this.objbn = "";
-      this.nobjbn = "";
-      this.countryCode = "";
-      this.date_info = new Date().toISOString().split("T")[0];
-      this.source = "";
-      this.pipetteStart = false;
-      this.pipetteEnd = false;
-      this.idError = false;
-      this.funktionError = false;
-      this.startrhmError = false;
-      this.endrhmError = false;
-      this.toleranceError = false;
-      this.objbnError = false;
-      this.nobjbnError = false;
-      this.date_infoError = false;
-      this.sourceError = false;
-      this.countryCodeError = false;
-    },
-    startEdit() {
-      this.clean();
-      this.edit = true;
-    },
-    togglePipette(t) {
-      this.openLayersMap()
-        .getLayer("DISTANCEMARKSAXIS")
-        .setVisible(true);
-      if (t === "start") {
-        this.$store.commit("map/mapPopupEnabled", this.pipetteStart);
-        this.pipetteStart = !this.pipetteStart;
-        this.pipetteEnd = false;
-      } else {
-        this.$store.commit("map/mapPopupEnabled", this.pipetteEnd);
-        this.pipetteEnd = !this.pipetteEnd;
-        this.pipetteStart = false;
-      }
-    },
-    validate() {
-      const fields = [
-        "id",
-        "funktion",
-        "startrhm",
-        "tolerance",
-        "endrhm",
-        "objbn",
-        "nobjbn",
-        "countryCode",
-        "date_info",
-        "source"
-      ];
-      fields.forEach(field => {
-        if (!this[field]) {
-          this[field + "Error"] = true;
-        } else {
-          this[field + "Error"] = false;
-        }
-      });
-    },
-    save() {
-      this.validate();
-      if (
-        !this.id ||
-        !this.startrhm ||
-        !this.endrhm ||
-        (!this.tolerance && this.editExistingStretch) ||
-        !this.source ||
-        !this.date_info ||
-        !this.objbn ||
-        !this.countryCode
-      )
-        return;
-      const data = {
-        name: this.id,
-        from: this.startrhm,
-        to: this.endrhm,
-        "source-organization": this.source,
-        "date-info": this.date_info,
-        objnam: this.objbn,
-        nobjnam: this.nobjbn,
-        countries: this.countryCode.split(",").map(x => {
-          return x.trim();
-        })
-      };
-      if (!this.editExistingStretch) {
-        data["tolerance"] = this.tolerance;
-      }
-      this.$store
-        .dispatch("imports/saveStretch", data)
-        .then(() => {
-          displayInfo({
-            title: this.$gettext("Import"),
-            message: this.$gettext("Starting import of stretch")
-          });
-          this.clean();
-          this.$store.dispatch("imports/loadStretches").then(() => {
-            this.edit = false;
-          });
-        })
-        .catch(error => {
-          const { status, data } = error.response;
-          displayError({
-            title: this.$gettext("Backend Error"),
-            message: `${status}: ${data.message || data}`
-          });
-        });
-    }
-  },
-  mounted() {
-    this.edit = false;
-    this.loading = true;
-    this.$store
-      .dispatch("imports/loadStretches")
-      .catch(error => {
-        const { status, data } = error.response;
-        displayError({
-          title: this.$gettext("Backend Error"),
-          message: `${status}: ${data.message || data}`
-        });
-      })
-      .finally(() => (this.loading = false));
-    this.loadStagingData().catch(error => {
-      const { status, data } = error.response;
-      displayError({
-        title: this.$gettext("Backend Error"),
-        message: `${status}: ${data.message || data}`
-      });
-    });
-  }
-};
-</script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/stretches/Stretches.vue	Wed May 15 12:04:14 2019 +0200
@@ -0,0 +1,605 @@
+<template>
+  <div class="d-flex flex-column mb-3">
+    <UIBoxHeader
+      icon="road"
+      :title="defineStretchesLabel"
+      :closeCallback="$parent.close"
+    />
+    <div class="position-relative">
+      <UISpinnerOverlay v-if="loading" />
+      <div v-if="!edit" class="mb-3">
+        <UITableHeader
+          :columns="[
+            { id: 'properties.name', title: `${nameLabel}`, class: 'col-4' },
+            {
+              id: 'properties.date_info',
+              title: `${dateLabel}`,
+              class: 'col-2'
+            },
+            {
+              id: 'properties.source_organization',
+              title: `${sourceorganizationLabel}`,
+              class: 'col-3'
+            }
+          ]"
+        />
+        <UITableBody
+          :data="filteredStretches() | sortTable(sortColumn, sortDirection)"
+        >
+          <template v-slot:row="{ item: stretch }">
+            <div class="py-1 col-4 ">
+              <a
+                class="pointer text-info"
+                v-if="isInStaging(stretch.properties.name)"
+                @click="gotoStaging(getStagingLink(stretch.properties.name))"
+              >
+                {{ stretch.properties.name
+                }}<font-awesome-icon
+                  class="ml-1 text-danger"
+                  icon="exclamation-triangle"
+                  fixed-width
+                ></font-awesome-icon
+                ><small class="ml-1">review</small>
+              </a>
+              <a v-else @click="moveMapToStretch(stretch)" href="#">{{
+                stretch.properties.name
+              }}</a>
+            </div>
+            <div class="py-1 col-2">
+              {{ stretch.properties.date_info | surveyDate }}
+            </div>
+            <div class="py-1 col-3">
+              {{ stretch.properties.source_organization }}
+            </div>
+            <div class="py-1 col text-right">
+              <button
+                class="btn btn-xs btn-dark mr-1"
+                @click="editStretch(stretch)"
+              >
+                <font-awesome-icon icon="pencil-alt" fixed-width />
+              </button>
+              <button
+                class="btn btn-xs btn-dark"
+                @click="deleteStretch(stretch)"
+              >
+                <font-awesome-icon icon="trash" fixed-width />
+              </button>
+            </div>
+          </template>
+        </UITableBody>
+      </div>
+      <div v-if="edit">
+        <div class="ml-3 mr-3">
+          <div class="d-flex flex-row justify-content-between">
+            <div class="mt-2 w-50 mr-2 text-left">
+              <small class="text-muted"> <translate>ID</translate> </small>
+              <input
+                id="id"
+                type="text"
+                class="form-control"
+                placeholder="AT_Section_12"
+                aria-label="id"
+                v-model="id"
+                :disabled="editExistingStretch"
+              />
+              <span class="text-left text-danger">
+                <small v-if="idError && !id">
+                  <translate>Please enter an id</translate>
+                </small>
+              </span>
+            </div>
+            <div class="mt-2 w-50 ml-2 text-left">
+              <div>
+                <small class="text-muted">
+                  <translate>Countrycode</translate>
+                </small>
+                <input
+                  id="countryCode"
+                  type="text"
+                  class="form-control"
+                  placeholder="AT"
+                  aria-label="id"
+                  v-model="countryCode"
+                />
+                <span class="text-left text-danger">
+                  <small v-if="countryCodeError && !countryCode">
+                    <translate>Please enter a countrycode </translate>
+                  </small>
+                </span>
+              </div>
+              <div class="w-50 ml-2"></div>
+            </div>
+          </div>
+          <div class="d-flex flex-column  justify-content-between">
+            <div class="mt-2 text-left">
+              <small class="text-muted">
+                <translate>Start rhm</translate>
+              </small>
+              <div class="d-flex flex-row">
+                <input
+                  id="startrhm"
+                  type="text"
+                  class="form-control"
+                  placeholder="e.g. ATXXX000010000019900"
+                  aria-label="startrhm"
+                  v-model="startrhm"
+                />
+                <span class="input-group-text" @click="togglePipette('start')">
+                  <font-awesome-icon
+                    :class="{ 'text-info': pipetteStart }"
+                    icon="crosshairs"
+                  />
+                </span>
+              </div>
+              <span class="text-left text-danger">
+                <small v-if="startrhmError && !startrhm">
+                  <translate>Please enter a start point</translate>
+                </small>
+              </span>
+            </div>
+            <div class="mt-2 text-left">
+              <small class="text-muted"> <translate>End rhm</translate> </small>
+              <div class="d-flex flex-row">
+                <input
+                  id="endrhm"
+                  type="text"
+                  class="form-control"
+                  placeholder="e.g. ATXXX000010000019900"
+                  aria-label="endrhm"
+                  v-model="endrhm"
+                />
+                <span class="input-group-text" @click="togglePipette('end')">
+                  <font-awesome-icon
+                    :class="{ 'text-info': pipetteEnd }"
+                    icon="crosshairs"
+                  />
+                </span>
+              </div>
+              <span class="text-left text-danger">
+                <small v-if="endrhmError && !endrhm">
+                  <translate>Please enter an end point</translate>
+                </small>
+              </span>
+            </div>
+          </div>
+          <div
+            v-if="!editExistingStretch"
+            class="d-flex flex-row justify-content-between"
+          >
+            <div class="mt-2  mr-2 w-50  text-left">
+              <small class="text-muted">
+                <translate
+                  >Tolerance for snapping of waterway axis [m]</translate
+                >
+              </small>
+              <input
+                class="form-control"
+                v-model.number="tolerance"
+                placeholder=""
+                type="number"
+                min="0"
+                step="any"
+                aria-label="tolerance"
+                id="tolerance"
+              />
+              <span class="text-left text-danger">
+                <small v-if="toleranceError && !tolerance">
+                  <translate>Please enter a tolerance value</translate>
+                </small>
+              </span>
+            </div>
+          </div>
+          <div class="d-flex flex-row justify-content-between">
+            <div class="mt-2  mr-2 w-50  text-left">
+              <small class="text-muted">
+                <translate>Object name</translate>
+              </small>
+              <input
+                id="objbn"
+                type="text"
+                class="form-control"
+                placeholder=""
+                aria-label="objbn"
+                v-model="objbn"
+              />
+              <span class="text-left text-danger">
+                <small v-if="objbnError && !objbn">
+                  <translate>Please enter an objectname</translate>
+                </small>
+              </span>
+            </div>
+            <div class="mt-2  ml-2 w-50  text-left">
+              <small class="text-muted">
+                <translate>National Object name</translate>
+              </small>
+              <input
+                id="nobjbn"
+                type="text"
+                class="form-control"
+                placeholder=""
+                aria-label="nobjbn"
+                v-model="nobjbn"
+              />
+            </div>
+          </div>
+          <div class="d-flex flex-row justify-content-between">
+            <div class="mt-2 mr-2 w-50 text-left">
+              <small class="text-muted">
+                <translate>Date info</translate>
+              </small>
+              <input
+                id="date_info"
+                type="date"
+                class="form-control"
+                placeholder="date_info"
+                aria-label="date_info"
+                v-model="date_info"
+              />
+              <span class="text-left text-danger">
+                <small v-if="date_infoError && !date_info">
+                  <translate>Please enter a date</translate>
+                </small>
+              </span>
+            </div>
+            <div class="mt-2 ml-2 w-50 text-left">
+              <small class="text-muted"> <translate>Source</translate> </small>
+              <input
+                id="source"
+                type="text"
+                class="form-control"
+                placeholder="source"
+                aria-label="source"
+                v-model="source"
+              />
+              <span class="text-left text-danger">
+                <small v-if="sourceError && !source">
+                  <translate>Please enter a source</translate>
+                </small>
+              </span>
+            </div>
+          </div>
+        </div>
+        <div class="text-right mt-2 mr-3 mb-3">
+          <button @click="edit = false" class="btn btn-warning mr-2">
+            Back
+          </button>
+          <button
+            @click="save"
+            type="submit"
+            class="shadow-sm btn btn-info submit-button"
+          >
+            <translate>Submit</translate>
+          </button>
+        </div>
+      </div>
+      <div class="text-right mr-3">
+        <button v-if="!edit" @click="startEdit()" class="btn btn-info">
+          <translate>New stretch</translate>
+        </button>
+      </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, 2019 by via donau
+ *   – Österreichische Wasserstraßen-Gesellschaft mbH
+ * Software engineering by Intevation GmbH
+ *
+ * Author(s):
+ * Thomas Junk <thomas.junk@intevation.de>
+ * Tom Gottfried <tom.gottfried@intevation.de>
+ */
+import { mapState, mapGetters } from "vuex";
+import { displayError, displayInfo } from "@/lib/errors";
+import { HTTP } from "@/lib/http";
+import { sortTable } from "@/lib/mixins";
+
+export default {
+  name: "importstretches",
+  mixins: [sortTable],
+  data() {
+    return {
+      staging: [],
+      edit: false,
+      editExistingStretch: false,
+      id: "",
+      funktion: "",
+      startrhm: "",
+      endrhm: "",
+      tolerance: 5,
+      objbn: "",
+      nobjbn: "",
+      countryCode: "",
+      date_info: new Date().toISOString().split("T")[0],
+      source: "",
+      pipetteStart: false,
+      pipetteEnd: false,
+      idError: false,
+      funktionError: false,
+      startrhmError: false,
+      endrhmError: false,
+      toleranceError: false,
+      objbnError: false,
+      nobjbnError: false,
+      date_infoError: false,
+      sourceError: false,
+      countryCodeError: false,
+      loading: false
+    };
+  },
+  computed: {
+    ...mapState("application", ["searchQuery"]),
+    ...mapState("map", ["identifiedFeatures", "currentMeasurement"]),
+    ...mapGetters("map", ["openLayersMap"]),
+    ...mapGetters("user", ["isSysAdmin"]),
+    ...mapState("imports", ["stretches"]),
+    defineStretchesLabel() {
+      return this.$gettext("Define Stretches");
+    },
+    nameLabel() {
+      return this.$gettext("Name");
+    },
+    dateLabel() {
+      return this.$gettext("Date");
+    },
+    sourceorganizationLabel() {
+      return this.$gettext("Source organization");
+    },
+    stretchesInStaging() {
+      const result = [];
+      for (let stretch of this.stretches) {
+        for (let s of this.staging) {
+          if (s.kind == "st" && s.summary.stretch == stretch.properties.name) {
+            result.push({ name: s.summary.stretch, id: s.id });
+          }
+        }
+      }
+      return result;
+    }
+  },
+  watch: {
+    identifiedFeatures() {
+      const distanceMark = this.identifiedFeatures.find(x =>
+        /^distance_marks_geoserver/.test(x["id_"])
+      );
+      if (distanceMark) {
+        const location = distanceMark.get("location");
+        this.startrhm = this.pipetteStart ? location : this.startrhm;
+        this.endrhm = this.pipetteEnd ? location : this.endrhm;
+        this.pipetteStart = false;
+        this.pipetteEnd = false;
+        this.$store.commit("map/mapPopupEnabled", true);
+      }
+    }
+  },
+  methods: {
+    filteredStretches() {
+      return this.stretches.filter(s => {
+        return (s.properties.name + s.properties.source_organization)
+          .toLowerCase()
+          .includes(this.searchQuery.toLowerCase());
+      });
+    },
+    gotoStaging(id) {
+      this.$router.push("/review/" + id);
+    },
+    isInStaging(stretchname) {
+      for (let s of this.stretchesInStaging) {
+        if (s.name == stretchname) return true;
+      }
+      return false;
+    },
+    getStagingLink(stretchname) {
+      for (let s of this.stretchesInStaging) {
+        if (s.name == stretchname) return s.id;
+      }
+    },
+    loadStagingData() {
+      return new Promise((resolve, reject) => {
+        HTTP.get("/imports?states=pending", {
+          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+        })
+          .then(response => {
+            const { imports } = response.data;
+            this.staging = imports;
+            resolve(response);
+          })
+          .catch(error => {
+            reject(error);
+          });
+      });
+    },
+    editStretch(stretch) {
+      const properties = stretch.properties;
+      this.date_info = properties.date_info.split("T")[0];
+      this.id = properties.name;
+      this.nobjbn = properties.nobjnam;
+      this.objbn = properties.objnam;
+      this.countryCode = properties.countries;
+      this.source = properties["source_organization"];
+      this.edit = true;
+      this.startrhm = properties.lower;
+      this.endrhm = properties.upper;
+      this.editExistingStretch = true;
+    },
+    deleteStretch(stretch) {
+      this.$store.commit("application/popup", {
+        icon: "trash",
+        title: this.$gettext("Delete Stretch"),
+        content:
+          this.$gettext("Do you really want to delete this stretch:") +
+          `<br>
+        <b>${stretch.properties.name}, ${
+            stretch.properties.source_organization
+          } (${stretch.properties.countries})</b>`,
+        confirm: {
+          label: this.$gettext("Delete"),
+          icon: "trash",
+          callback: () => {
+            displayInfo({
+              title: this.$gettext("Not implemented"),
+              message: this.$gettext("Deleting ") + stretch.id
+            });
+          }
+        },
+        cancel: {
+          label: this.$gettext("Cancel"),
+          icon: "times"
+        }
+      });
+    },
+    moveMapToStretch(stretch) {
+      this.$store.commit("imports/selectedStretchId", stretch.id);
+      this.openLayersMap()
+        .getLayer("STRETCHES")
+        .setVisible(true);
+      this.$store.dispatch("map/moveToFeauture", {
+        feature: stretch,
+        zoom: 17,
+        preventZoomOut: true
+      });
+    },
+    clean() {
+      this.id = "";
+      this.edit = false;
+      this.editExistingStretch = false;
+      this.funktion = "";
+      this.startrhm = "";
+      this.tolerance = 5;
+      this.endrhm = "";
+      this.objbn = "";
+      this.nobjbn = "";
+      this.countryCode = "";
+      this.date_info = new Date().toISOString().split("T")[0];
+      this.source = "";
+      this.pipetteStart = false;
+      this.pipetteEnd = false;
+      this.idError = false;
+      this.funktionError = false;
+      this.startrhmError = false;
+      this.endrhmError = false;
+      this.toleranceError = false;
+      this.objbnError = false;
+      this.nobjbnError = false;
+      this.date_infoError = false;
+      this.sourceError = false;
+      this.countryCodeError = false;
+    },
+    startEdit() {
+      this.clean();
+      this.edit = true;
+    },
+    togglePipette(t) {
+      this.openLayersMap()
+        .getLayer("DISTANCEMARKSAXIS")
+        .setVisible(true);
+      if (t === "start") {
+        this.$store.commit("map/mapPopupEnabled", this.pipetteStart);
+        this.pipetteStart = !this.pipetteStart;
+        this.pipetteEnd = false;
+      } else {
+        this.$store.commit("map/mapPopupEnabled", this.pipetteEnd);
+        this.pipetteEnd = !this.pipetteEnd;
+        this.pipetteStart = false;
+      }
+    },
+    validate() {
+      const fields = [
+        "id",
+        "funktion",
+        "startrhm",
+        "tolerance",
+        "endrhm",
+        "objbn",
+        "nobjbn",
+        "countryCode",
+        "date_info",
+        "source"
+      ];
+      fields.forEach(field => {
+        if (!this[field]) {
+          this[field + "Error"] = true;
+        } else {
+          this[field + "Error"] = false;
+        }
+      });
+    },
+    save() {
+      this.validate();
+      if (
+        !this.id ||
+        !this.startrhm ||
+        !this.endrhm ||
+        (!this.tolerance && this.editExistingStretch) ||
+        !this.source ||
+        !this.date_info ||
+        !this.objbn ||
+        !this.countryCode
+      )
+        return;
+      const data = {
+        name: this.id,
+        from: this.startrhm,
+        to: this.endrhm,
+        "source-organization": this.source,
+        "date-info": this.date_info,
+        objnam: this.objbn,
+        nobjnam: this.nobjbn,
+        countries: this.countryCode.split(",").map(x => {
+          return x.trim();
+        })
+      };
+      if (!this.editExistingStretch) {
+        data["tolerance"] = this.tolerance;
+      }
+      this.$store
+        .dispatch("imports/saveStretch", data)
+        .then(() => {
+          displayInfo({
+            title: this.$gettext("Import"),
+            message: this.$gettext("Starting import of stretch")
+          });
+          this.clean();
+          this.$store.dispatch("imports/loadStretches").then(() => {
+            this.edit = false;
+          });
+        })
+        .catch(error => {
+          const { status, data } = error.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status}: ${data.message || data}`
+          });
+        });
+    }
+  },
+  mounted() {
+    this.edit = false;
+    this.loading = true;
+    this.$store
+      .dispatch("imports/loadStretches")
+      .catch(error => {
+        const { status, data } = error.response;
+        displayError({
+          title: this.$gettext("Backend Error"),
+          message: `${status}: ${data.message || data}`
+        });
+      })
+      .finally(() => (this.loading = false));
+    this.loadStagingData().catch(error => {
+      const { status, data } = error.response;
+      displayError({
+        title: this.$gettext("Backend Error"),
+        message: `${status}: ${data.message || data}`
+      });
+    });
+  }
+};
+</script>