Mercurial > gemma
diff client/src/components/stretches/Stretches.vue @ 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 | |
children | f92f7c9df392 |
line wrap: on
line diff
--- /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>