Mercurial > gemma
changeset 3292:7a88b37bce8b
merge
author | Thomas Junk <thomas.junk@intevation.de> |
---|---|
date | Thu, 16 May 2019 14:15:45 +0200 |
parents | 6996aa1d2df9 (current diff) 3ada3d0347bd (diff) |
children | 76f643d20f19 |
files | |
diffstat | 5 files changed, 1068 insertions(+), 401 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/sections/SectionForm.vue Thu May 16 14:15:45 2019 +0200 @@ -0,0 +1,360 @@ +<template> + <div class="d-flex flex-column"> + <div class="d-flex justify-content-between mt-2 px-2"> + <div class="text-left flex-fill mr-1"> + <small class="text-muted"> + <translate>ID</translate> + </small> + <input + id="id" + type="text" + class="form-control form-control-sm" + placeholder="AT_Section_12" + v-model="id" + :disabled="editSection" + /> + <span class="text-left text-danger"> + <small v-if="errors.id && !id"> + <translate>Please enter an id</translate> + </small> + </span> + </div> + </div> + <div class="d-flex justify-content-between mt-2 px-2"> + <div class="text-left flex-fill"> + <small class="text-muted"> + <translate>Start rhm</translate> + </small> + <div class="d-flex flex-row position-relative"> + <input + id="startrhm" + type="text" + class="form-control form-control-sm" + placeholder="e.g. ATXXX000010000019900" + v-model="startrhm" + ref="startrhm" + @focus="enablePipette('start')" + @blur="disablePipette('start')" + /> + <span + class="input-group-text position-absolute input-button" + @click="$refs.startrhm.focus()" + v-tooltip="pipetteTooltip" + > + <font-awesome-icon + :class="{ 'text-info': pipetteStart }" + icon="crosshairs" + /> + </span> + </div> + <span class="text-left text-danger"> + <small v-if="errors.startrhm && !startrhm"> + <translate>Please enter a start point</translate> + </small> + </span> + </div> + <div class="text-left flex-fill ml-2"> + <small class="text-muted"> + <translate>End rhm</translate> + </small> + <div class="d-flex flex-row position-relative"> + <input + id="endrhm" + type="text" + class="form-control form-control-sm" + placeholder="e.g. ATXXX000010000019900" + v-model="endrhm" + ref="endrhm" + @focus="enablePipette('end')" + @blur="disablePipette('end')" + /> + <span + class="input-group-text position-absolute input-button" + @click="$refs.endrhm.focus()" + v-tooltip="pipetteTooltip" + > + <font-awesome-icon + :class="{ 'text-info': pipetteEnd }" + icon="crosshairs" + /> + </span> + </div> + <span class="text-left text-danger"> + <small v-if="errors.endrhm && !endrhm"> + <translate>Please enter an end point</translate> + </small> + </span> + </div> + <div class="text-left ml-2" v-if="!editSection"> + <small class="text-muted"> + <translate>Tolerance for snapping to axis</translate> + </small> + <div class="d-flex flex-row position-relative"> + <input + class="form-control form-control-sm" + v-model.number="tolerance" + type="number" + min="0" + step="any" + id="tolerance" + /> + <span class="input-group-text position-absolute input-button"> + m + </span> + </div> + <span class="text-left text-danger"> + <small v-if="errors.tolerance && !tolerance"> + <translate>Please enter a tolerance value</translate> + </small> + </span> + </div> + </div> + <div class="d-flex flex-row justify-content-between px-2"> + <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 form-control-sm" + placeholder="" + v-model="objbn" + /> + <span class="text-left text-danger"> + <small v-if="errors.objbn && !objbn"> + <translate>Please enter an objectname</translate> + </small> + </span> + </div> + <div class="mt-2 w-50 text-left"> + <small class="text-muted"> + <translate>National Object name</translate> + </small> + <input + id="nobjbn" + type="text" + class="form-control form-control-sm" + v-model="nobjbn" + /> + </div> + </div> + <div class="d-flex flex-row justify-content-between px-2"> + <div class="mt-2 w-50 text-left"> + <small class="text-muted"> + <translate>Date info</translate> + </small> + <input + id="date_info" + type="date" + class="form-control form-control-sm" + placeholder="date_info" + v-model="date_info" + /> + <span class="text-left text-danger"> + <small v-if="errors.date_info && !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 Organization</translate> + </small> + <input + id="source_organization" + type="text" + class="form-control form-control-sm" + v-model="source_organization" + /> + <span class="text-left text-danger"> + <small v-if="errors.source_organization && !source_organization"> + <translate>Please enter a source organization</translate> + </small> + </span> + </div> + </div> + <div class="d-flex justify-content-between mt-2 p-2 border-top"> + <button @click="$parent.showForm = false" class="btn btn-sm btn-warning"> + <translate>Back</translate> + </button> + <button + @click="save" + type="submit" + class="shadow-sm btn btn-sm btn-info submit-button" + > + <translate>Submit</translate> + </button> + </div> + </div> +</template> + +<style lang="sass" scoped> +.input-button + border-top-left-radius: 0 + border-bottom-left-radius: 0 + right: 0 + height: 31px +</style> + +<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> + * Markus Kottländer <markus.kottlaender@intevation.de> + */ +import { mapState, mapGetters } from "vuex"; +import { displayError, displayInfo } from "@/lib/errors"; +import { sortTable } from "@/lib/mixins"; + +export default { + mixins: [sortTable], + props: ["editSection"], + data() { + return { + pipetteStart: false, + pipetteEnd: false, + id: null, + startrhm: null, + endrhm: null, + tolerance: 5, + objbn: null, + nobjbn: null, + date_info: new Date().toISOString().split("T")[0], + source_organization: null, + errors: { + id: false, + startrhm: false, + endrhm: false, + tolerance: false, + objbn: false, + nobjbn: false, + date_info: false, + source_organization: false + } + }; + }, + computed: { + ...mapState("map", ["identifiedFeatures"]), + ...mapGetters("map", ["openLayersMap"]), + pipetteTooltip() { + return this.$gettext("Choose a distance mark by clicking on the map."); + } + }, + 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: { + enablePipette(t) { + this.openLayersMap() + .getLayer("DISTANCEMARKSAXIS") + .setVisible(true); + this.$store.commit("map/mapPopupEnabled", false); + if (t === "start") { + this.pipetteStart = true; + this.pipetteEnd = false; + } else { + this.pipetteStart = false; + this.pipetteEnd = true; + } + }, + disablePipette() { + this.$store.commit("map/mapPopupEnabled", true); + this.pipetteStart = false; + this.pipetteEnd = false; + }, + validate() { + const fields = [ + "id", + "startrhm", + "endrhm", + "objbn", + "date_info", + "source_organization" + ]; + if (!this.editSection) fields.push("tolerance"); + fields.forEach(field => { + if (!this[field]) { + this.errors[field] = true; + } else { + this.errors[field] = false; + } + }); + + // return true if no errors + return !Object.values(this.errors).reduce((a, b) => a + b, 0); + }, + save() { + if (this.validate()) { + const data = { + name: this.id, + from: this.startrhm, + to: this.endrhm, + "source-organization": this.source_organization, + "date-info": this.date_info, + objnam: this.objbn, + nobjnam: this.nobjbn + }; + if (!this.editSection) { + data["tolerance"] = this.tolerance; + } + this.$parent.loading = true; + this.$store + .dispatch("imports/saveSection", data) + .then(() => { + displayInfo({ + title: this.$gettext("Import"), + message: this.$gettext("Starting import of section") + }); + this.$store.dispatch("imports/loadSections").then(() => { + this.$parent.loading = false; + this.$parent.showForm = false; + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + } + } + }, + mounted() { + if (this.editSection) { + const props = this.editSection.properties; + this.id = props.name; + this.startrhm = props.lower.replace(/[,()]/g, ""); + this.endrhm = props.upper.replace(/[,()]/g, ""); + this.tolerance = props.tolerance; + this.objbn = props.objnam; + this.nobjbn = props.nobjnam; + this.date_info = props.date_info.split("T")[0]; + this.source_organization = props.source_organization; + } + } +}; +</script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/sections/Sections.vue Thu May 16 14:15:45 2019 +0200 @@ -0,0 +1,249 @@ +<template> + <div class="d-flex flex-column"> + <UIBoxHeader icon="road" :title="title" :closeCallback="$parent.close" /> + <div class="position-relative"> + <UISpinnerOverlay v-if="loading" /> + <SectionForm v-if="showForm" :editSection="editSection" /> + <div v-else> + <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="filteredSections() | sortTable(sortColumn, sortDirection)" + > + <template v-slot:row="{ item: section }"> + <div class="py-1 px-2 col-4"> + <a @click="moveMapToSection(section)" href="#"> + {{ section.properties.name }} + </a> + </div> + <div class="py-1 px-2 col-2"> + {{ section.properties.date_info | surveyDate }} + </div> + <div class="py-1 px-2 col-3"> + {{ section.properties.source_organization }} + </div> + <div class="py-1 px-2 col text-right"> + <button + v-if="isInStaging(section.properties.name)" + @click="gotoStaging(section.properties.name)" + class="btn btn-xs btn-danger mr-1" + > + <font-awesome-icon + icon="exclamation-triangle" + fixed-width + v-tooltip="reviewTooltip" + /> + </button> + <button + class="btn btn-xs btn-dark mr-1" + @click=" + showForm = true; + editSection = section; + " + > + <font-awesome-icon icon="pencil-alt" fixed-width /> + </button> + <button + class="btn btn-xs btn-dark" + @click="deleteSection(section)" + > + <font-awesome-icon icon="trash" fixed-width /> + </button> + </div> + </template> + </UITableBody> + <div class="text-right p-2 border-top"> + <button + @click=" + showForm = true; + editSection = null; + " + class="btn btn-sm btn-info" + > + <translate>New section</translate> + </button> + </div> + </div> + </div> + </div> +</template> + +<style lang="sass" scoped> +.input-button + border-top-left-radius: 0 + border-bottom-left-radius: 0 + right: 0 + height: 31px +</style> + +<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 { + mixins: [sortTable], + components: { + SectionForm: () => import("./SectionForm") + }, + data() { + return { + staging: [], + loading: false, + showForm: false, + editSection: null + }; + }, + computed: { + ...mapState("application", ["searchQuery"]), + ...mapGetters("map", ["openLayersMap"]), + ...mapState("imports", ["sections"]), + title() { + return this.$gettext("Define Sections"); + }, + nameLabel() { + return this.$gettext("Name"); + }, + dateLabel() { + return this.$gettext("Date"); + }, + sourceorganizationLabel() { + return this.$gettext("Source organization"); + }, + reviewTooltip() { + return this.$gettext("Review pending import"); + } + }, + methods: { + filteredSections() { + return this.sections.filter(s => { + return (s.properties.name + s.properties.source_organization) + .toLowerCase() + .includes(this.searchQuery.toLowerCase()); + }); + }, + gotoStaging(sectionName) { + let pendingImport = this.staging.find(s => s.name === sectionName); + if (pendingImport) + this.$router.push("/imports/overview/" + pendingImport.id); + }, + isInStaging(sectionName) { + return !!this.staging.find(s => s.name === sectionName); + }, + loadStagingData() { + HTTP.get("/imports?states=pending&kinds=sec", { + headers: { "X-Gemma-Auth": localStorage.getItem("token") } + }) + .then(response => { + response.data.imports.forEach(i => { + HTTP.get("/imports/" + i.id, { + headers: { "X-Gemma-Auth": localStorage.getItem("token") } + }) + .then(response => { + this.staging.push({ + id: i.id, + name: response.data.summary.section + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }) + .finally(() => (this.loading = false)); + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + }, + deleteSection(section) { + this.$store.commit("application/popup", { + icon: "trash", + title: this.$gettext("Delete Section"), + content: + this.$gettext("Do you really want to delete this section:") + + `<br> + <b>${section.properties.name}, ${ + section.properties.source_organization + } (${section.properties.countries})</b>`, + confirm: { + label: this.$gettext("Delete"), + icon: "trash", + callback: () => { + displayInfo({ + title: this.$gettext("Not implemented"), + message: this.$gettext("Deleting ") + section.id + }); + } + }, + cancel: { + label: this.$gettext("Cancel"), + icon: "times" + } + }); + }, + moveMapToSection(section) { + this.$store.commit("imports/selectedSectionId", section.id); + this.$store.commit("fairwayavailability/type", "sections"); + this.$store.commit("application/showFairwayDepth", true); + this.openLayersMap() + .getLayer("SECTIONS") + .setVisible(true); + this.$store.dispatch("map/moveToFeauture", { + feature: section, + zoom: 17, + preventZoomOut: true + }); + } + }, + mounted() { + this.loading = true; + this.$store + .dispatch("imports/loadSections") + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }) + .finally(() => (this.loading = false)); + this.loadStagingData(); + } +}; +</script>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/client/src/components/stretches/StretchForm.vue Thu May 16 14:15:45 2019 +0200 @@ -0,0 +1,384 @@ +<template> + <div class="d-flex flex-column"> + <div class="d-flex justify-content-between mt-2 px-2"> + <div class="text-left flex-fill mr-1"> + <small class="text-muted"> + <translate>ID</translate> + </small> + <input + id="id" + type="text" + class="form-control form-control-sm" + placeholder="AT_Section_12" + v-model="id" + :disabled="editStretch" + /> + <span class="text-left text-danger"> + <small v-if="errors.id && !id"> + <translate>Please enter an id</translate> + </small> + </span> + </div> + <div class="text-left flex-fill ml-1"> + <small class="text-muted"> + <translate>Countrycode</translate> + </small> + <input + id="countryCode" + type="text" + class="form-control form-control-sm" + placeholder="AT" + v-model="countryCode" + /> + <span class="text-left text-danger"> + <small v-if="errors.countryCode && !countryCode"> + <translate>Please enter a countrycode </translate> + </small> + </span> + </div> + </div> + <div class="d-flex justify-content-between mt-2 px-2"> + <div class="text-left flex-fill"> + <small class="text-muted"> + <translate>Start rhm</translate> + </small> + <div class="d-flex flex-row position-relative"> + <input + id="startrhm" + type="text" + class="form-control form-control-sm" + placeholder="e.g. ATXXX000010000019900" + v-model="startrhm" + ref="startrhm" + @focus="enablePipette('start')" + @blur="disablePipette('start')" + /> + <span + class="input-group-text position-absolute input-button" + @click="$refs.startrhm.focus()" + v-tooltip="pipetteTooltip" + > + <font-awesome-icon + :class="{ 'text-info': pipetteStart }" + icon="crosshairs" + /> + </span> + </div> + <span class="text-left text-danger"> + <small v-if="errors.startrhm && !startrhm"> + <translate>Please enter a start point</translate> + </small> + </span> + </div> + <div class="text-left flex-fill ml-2"> + <small class="text-muted"> + <translate>End rhm</translate> + </small> + <div class="d-flex flex-row position-relative"> + <input + id="endrhm" + type="text" + class="form-control form-control-sm" + placeholder="e.g. ATXXX000010000019900" + v-model="endrhm" + ref="endrhm" + @focus="enablePipette('end')" + @blur="disablePipette('end')" + /> + <span + class="input-group-text position-absolute input-button" + @click="$refs.endrhm.focus()" + v-tooltip="pipetteTooltip" + > + <font-awesome-icon + :class="{ 'text-info': pipetteEnd }" + icon="crosshairs" + /> + </span> + </div> + <span class="text-left text-danger"> + <small v-if="errors.endrhm && !endrhm"> + <translate>Please enter an end point</translate> + </small> + </span> + </div> + <div class="text-left ml-2" v-if="!editStretch"> + <small class="text-muted"> + <translate>Tolerance for snapping to axis</translate> + </small> + <div class="d-flex flex-row position-relative"> + <input + class="form-control form-control-sm" + v-model.number="tolerance" + type="number" + min="0" + step="any" + id="tolerance" + /> + <span class="input-group-text position-absolute input-button"> + m + </span> + </div> + <span class="text-left text-danger"> + <small v-if="errors.tolerance && !tolerance"> + <translate>Please enter a tolerance value</translate> + </small> + </span> + </div> + </div> + <div class="d-flex flex-row justify-content-between px-2"> + <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 form-control-sm" + placeholder="" + v-model="objbn" + /> + <span class="text-left text-danger"> + <small v-if="errors.objbn && !objbn"> + <translate>Please enter an objectname</translate> + </small> + </span> + </div> + <div class="mt-2 w-50 text-left"> + <small class="text-muted"> + <translate>National Object name</translate> + </small> + <input + id="nobjbn" + type="text" + class="form-control form-control-sm" + v-model="nobjbn" + /> + </div> + </div> + <div class="d-flex flex-row justify-content-between px-2"> + <div class="mt-2 w-50 text-left"> + <small class="text-muted"> + <translate>Date info</translate> + </small> + <input + id="date_info" + type="date" + class="form-control form-control-sm" + placeholder="date_info" + v-model="date_info" + /> + <span class="text-left text-danger"> + <small v-if="errors.date_info && !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 Organization</translate> + </small> + <input + id="source_organization" + type="text" + class="form-control form-control-sm" + v-model="source_organization" + /> + <span class="text-left text-danger"> + <small v-if="errors.source_organization && !source_organization"> + <translate>Please enter a source organization</translate> + </small> + </span> + </div> + </div> + <div class="d-flex justify-content-between mt-2 p-2 border-top"> + <button @click="$parent.showForm = false" class="btn btn-sm btn-warning"> + <translate>Back</translate> + </button> + <button + @click="save" + type="submit" + class="shadow-sm btn btn-sm btn-info submit-button" + > + <translate>Submit</translate> + </button> + </div> + </div> +</template> + +<style lang="sass" scoped> +.input-button + border-top-left-radius: 0 + border-bottom-left-radius: 0 + right: 0 + height: 31px +</style> + +<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> + * Markus Kottländer <markus.kottlaender@intevation.de> + */ +import { mapState, mapGetters } from "vuex"; +import { displayError, displayInfo } from "@/lib/errors"; +import { sortTable } from "@/lib/mixins"; + +export default { + mixins: [sortTable], + props: ["editStretch"], + data() { + return { + pipetteStart: false, + pipetteEnd: false, + id: null, + startrhm: null, + endrhm: null, + tolerance: 5, + objbn: null, + nobjbn: null, + date_info: new Date().toISOString().split("T")[0], + source_organization: null, + countryCode: null, + errors: { + id: false, + startrhm: false, + endrhm: false, + tolerance: false, + objbn: false, + nobjbn: false, + date_info: false, + source_organization: false, + countryCode: false + } + }; + }, + computed: { + ...mapState("map", ["identifiedFeatures"]), + ...mapGetters("map", ["openLayersMap"]), + pipetteTooltip() { + return this.$gettext("Choose a distance mark by clicking on the map."); + } + }, + 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: { + enablePipette(t) { + this.openLayersMap() + .getLayer("DISTANCEMARKSAXIS") + .setVisible(true); + this.$store.commit("map/mapPopupEnabled", false); + if (t === "start") { + this.pipetteStart = true; + this.pipetteEnd = false; + } else { + this.pipetteStart = false; + this.pipetteEnd = true; + } + }, + disablePipette() { + this.$store.commit("map/mapPopupEnabled", true); + this.pipetteStart = false; + this.pipetteEnd = false; + }, + validate() { + const fields = [ + "id", + "startrhm", + "endrhm", + "objbn", + "countryCode", + "date_info", + "source_organization" + ]; + if (!this.editStretch) fields.push("tolerance"); + fields.forEach(field => { + if (!this[field]) { + this.errors[field] = true; + } else { + this.errors[field] = false; + } + }); + + // return true if no errors + return !Object.values(this.errors).reduce((a, b) => a + b, 0); + }, + save() { + if (this.validate()) { + const data = { + name: this.id, + from: this.startrhm, + to: this.endrhm, + "source-organization": this.source_organization, + "date-info": this.date_info, + objnam: this.objbn, + nobjnam: this.nobjbn, + countries: this.countryCode.split(",").map(x => { + return x.trim(); + }) + }; + if (!this.editStretch) { + data["tolerance"] = this.tolerance; + } + this.$parent.loading = true; + this.$store + .dispatch("imports/saveStretch", data) + .then(() => { + displayInfo({ + title: this.$gettext("Import"), + message: this.$gettext("Starting import of stretch") + }); + this.$store.dispatch("imports/loadStretches").then(() => { + this.$parent.loading = false; + this.$parent.showForm = false; + }); + }) + .catch(error => { + const { status, data } = error.response; + displayError({ + title: this.$gettext("Backend Error"), + message: `${status}: ${data.message || data}` + }); + }); + } + } + }, + mounted() { + if (this.editStretch) { + const props = this.editStretch.properties; + this.id = props.name; + this.startrhm = props.lower.replace(/[,()]/g, ""); + this.endrhm = props.upper.replace(/[,()]/g, ""); + this.tolerance = props.tolerance; + this.objbn = props.objnam; + this.nobjbn = props.nobjnam; + this.date_info = props.date_info.split("T")[0]; + this.source_organization = props.source_organization; + this.countryCode = props.countries; + } + } +}; +</script>
--- a/client/src/components/stretches/Stretches.vue Thu May 16 12:53:46 2019 +0200 +++ b/client/src/components/stretches/Stretches.vue Thu May 16 14:15:45 2019 +0200 @@ -1,13 +1,10 @@ <template> <div class="d-flex flex-column"> - <UIBoxHeader - icon="road" - :title="defineStretchesLabel" - :closeCallback="$parent.close" - /> + <UIBoxHeader icon="road" :title="title" :closeCallback="$parent.close" /> <div class="position-relative"> <UISpinnerOverlay v-if="loading" /> - <div v-if="!edit"> + <StretchForm v-if="showForm" :editStretch="editStretch" /> + <div v-else> <UITableHeader :columns="[ { id: 'properties.name', title: `${nameLabel}`, class: 'col-4' }, @@ -52,7 +49,10 @@ </button> <button class="btn btn-xs btn-dark mr-1" - @click="editStretch(stretch)" + @click=" + showForm = true; + editStretch = stretch; + " > <font-awesome-icon icon="pencil-alt" fixed-width /> </button> @@ -66,224 +66,17 @@ </template> </UITableBody> <div class="text-right p-2 border-top"> - <button :key="1" @click="startEdit()" class="btn btn-sm btn-info"> + <button + @click=" + showForm = true; + editStretch = null; + " + class="btn btn-sm btn-info" + > <translate>New stretch</translate> </button> </div> </div> - <div v-if="edit"> - <div class="mx-2"> - <div class="d-flex justify-content-between mt-2"> - <div class="text-left flex-fill mr-1"> - <small class="text-muted"> - <translate>ID</translate> - </small> - <input - id="id" - type="text" - class="form-control form-control-sm" - 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="text-left flex-fill ml-1"> - <small class="text-muted"> - <translate>Countrycode</translate> - </small> - <input - id="countryCode" - type="text" - class="form-control form-control-sm" - 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> - <div class="d-flex justify-content-between mt-2"> - <div class="text-left flex-fill mr-1"> - <small class="text-muted"> - <translate>Start rhm</translate> - </small> - <div class="d-flex flex-row position-relative"> - <input - id="startrhm" - type="text" - class="form-control form-control-sm" - placeholder="e.g. ATXXX000010000019900" - aria-label="startrhm" - v-model="startrhm" - /> - <span - class="input-group-text position-absolute input-button" - @click="togglePipette('start')" - v-tooltip="pipetteTooltip" - > - <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="text-left flex-fill ml-1"> - <small class="text-muted"> - <translate>End rhm</translate> - </small> - <div class="d-flex flex-row position-relative"> - <input - id="endrhm" - type="text" - class="form-control form-control-sm" - placeholder="e.g. ATXXX000010000019900" - aria-label="endrhm" - v-model="endrhm" - /> - <span - class="input-group-text position-absolute input-button" - @click="togglePipette('end')" - v-tooltip="pipetteTooltip" - > - <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 form-control-sm" - 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 form-control-sm" - 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 form-control-sm" - 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 form-control-sm" - 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 form-control-sm" - 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="d-flex justify-content-between mt-2 p-2 border-top"> - <button :key="2" @click="edit = false" class="btn btn-sm btn-warning"> - Back - </button> - <button - @click="save" - type="submit" - class="shadow-sm btn btn-sm btn-info submit-button" - > - <translate>Submit</translate> - </button> - </div> - </div> </div> </div> </template> @@ -317,45 +110,23 @@ import { sortTable } from "@/lib/mixins"; export default { - name: "importstretches", mixins: [sortTable], + components: { + StretchForm: () => import("./StretchForm") + }, 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 + loading: false, + showForm: false, + editStretch: null }; }, computed: { ...mapState("application", ["searchQuery"]), - ...mapState("map", ["identifiedFeatures", "currentMeasurement"]), ...mapGetters("map", ["openLayersMap"]), - ...mapGetters("user", ["isSysAdmin"]), ...mapState("imports", ["stretches"]), - defineStretchesLabel() { + title() { return this.$gettext("Define Stretches"); }, nameLabel() { @@ -369,24 +140,6 @@ }, reviewTooltip() { return this.$gettext("Review pending import"); - }, - pipetteTooltip() { - return this.$gettext("Choose a distance mark by clicking on the map."); - } - }, - 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: { @@ -438,19 +191,6 @@ }); }); }, - 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.replace(/[,()]/g, ""); - this.endrhm = properties.upper.replace(/[,()]/g, ""); - this.editExistingStretch = true; - }, deleteStretch(stretch) { this.$store.commit("application/popup", { icon: "trash", @@ -489,123 +229,9 @@ 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")
--- a/client/src/store/imports.js Thu May 16 12:53:46 2019 +0200 +++ b/client/src/store/imports.js Thu May 16 14:15:45 2019 +0200 @@ -33,6 +33,8 @@ warning: false, stretches: [], selectedStretchId: null, + sections: [], + selectedSectionId: null, imports: [], reviewed: [], show: null, @@ -46,19 +48,19 @@ }; }; -const getStretchFromWFS = filter => { +const getFromWFS = (type, filter) => { return new Promise((resolve, reject) => { - var stretchesFeatureCollectionRequest = new WFS().writeGetFeature({ + var featureCollectionRequest = new WFS().writeGetFeature({ srsName: "EPSG:4326", featureNS: "gemma", featurePrefix: "gemma", - featureTypes: ["stretches_geoserver"], + featureTypes: [type], outputFormat: "application/json", filter: filter }); HTTP.post( "/internal/wfs", - new XMLSerializer().serializeToString(stretchesFeatureCollectionRequest), + new XMLSerializer().serializeToString(featureCollectionRequest), { headers: { "X-Gemma-Auth": localStorage.getItem("token"), @@ -132,6 +134,12 @@ selectedStretchId: (state, id) => { state.selectedStretchId = id; }, + setSections: (state, sections) => { + state.sections = sections; + }, + selectedSectionId: (state, id) => { + state.selectedSectionId = id; + }, setReviewed: (state, reviewed) => { state.reviewed = reviewed; }, @@ -194,7 +202,7 @@ actions: { loadStretch(context, name) { return new Promise((resolve, reject) => { - getStretchFromWFS(equalToFilter("name", name)) + getFromWFS("stretches_geoserver", equalToFilter("name", name)) .then(response => { resolve(response); }) @@ -205,7 +213,7 @@ }, loadStretches({ commit }) { return new Promise((resolve, reject) => { - getStretchFromWFS(equalToFilter("staging_done", true)) + getFromWFS("stretches_geoserver", equalToFilter("staging_done", true)) .then(response => { if (response.data.features) { commit("setStretches", response.data.features); @@ -232,6 +240,46 @@ }); }); }, + loadSection(context, name) { + return new Promise((resolve, reject) => { + getFromWFS("sections_geoserver", equalToFilter("name", name)) + .then(response => { + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, + loadSections({ commit }) { + return new Promise((resolve, reject) => { + getFromWFS("sections_geoserver", equalToFilter("staging_done", true)) + .then(response => { + if (response.data.features) { + commit("setSections", response.data.features); + } else { + commit("setSections", []); + } + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, + saveSection(context, section) { + return new Promise((resolve, reject) => { + HTTP.post("/imports/sec", section, { + headers: { "X-Gemma-Auth": localStorage.getItem("token") } + }) + .then(response => { + resolve(response); + }) + .catch(error => { + reject(error); + }); + }); + }, getImports({ commit }, options) { let { filter, from, to, query } = options; let queryParams = "";