comparison 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
comparison
equal deleted inserted replaced
3262:a2e9671f4ad6 3263:d23532a4d0c3
1 <template>
2 <div class="d-flex flex-column mb-3">
3 <UIBoxHeader
4 icon="road"
5 :title="defineStretchesLabel"
6 :closeCallback="$parent.close"
7 />
8 <div class="position-relative">
9 <UISpinnerOverlay v-if="loading" />
10 <div v-if="!edit" class="mb-3">
11 <UITableHeader
12 :columns="[
13 { id: 'properties.name', title: `${nameLabel}`, class: 'col-4' },
14 {
15 id: 'properties.date_info',
16 title: `${dateLabel}`,
17 class: 'col-2'
18 },
19 {
20 id: 'properties.source_organization',
21 title: `${sourceorganizationLabel}`,
22 class: 'col-3'
23 }
24 ]"
25 />
26 <UITableBody
27 :data="filteredStretches() | sortTable(sortColumn, sortDirection)"
28 >
29 <template v-slot:row="{ item: stretch }">
30 <div class="py-1 col-4 ">
31 <a
32 class="pointer text-info"
33 v-if="isInStaging(stretch.properties.name)"
34 @click="gotoStaging(getStagingLink(stretch.properties.name))"
35 >
36 {{ stretch.properties.name
37 }}<font-awesome-icon
38 class="ml-1 text-danger"
39 icon="exclamation-triangle"
40 fixed-width
41 ></font-awesome-icon
42 ><small class="ml-1">review</small>
43 </a>
44 <a v-else @click="moveMapToStretch(stretch)" href="#">{{
45 stretch.properties.name
46 }}</a>
47 </div>
48 <div class="py-1 col-2">
49 {{ stretch.properties.date_info | surveyDate }}
50 </div>
51 <div class="py-1 col-3">
52 {{ stretch.properties.source_organization }}
53 </div>
54 <div class="py-1 col text-right">
55 <button
56 class="btn btn-xs btn-dark mr-1"
57 @click="editStretch(stretch)"
58 >
59 <font-awesome-icon icon="pencil-alt" fixed-width />
60 </button>
61 <button
62 class="btn btn-xs btn-dark"
63 @click="deleteStretch(stretch)"
64 >
65 <font-awesome-icon icon="trash" fixed-width />
66 </button>
67 </div>
68 </template>
69 </UITableBody>
70 </div>
71 <div v-if="edit">
72 <div class="ml-3 mr-3">
73 <div class="d-flex flex-row justify-content-between">
74 <div class="mt-2 w-50 mr-2 text-left">
75 <small class="text-muted"> <translate>ID</translate> </small>
76 <input
77 id="id"
78 type="text"
79 class="form-control"
80 placeholder="AT_Section_12"
81 aria-label="id"
82 v-model="id"
83 :disabled="editExistingStretch"
84 />
85 <span class="text-left text-danger">
86 <small v-if="idError && !id">
87 <translate>Please enter an id</translate>
88 </small>
89 </span>
90 </div>
91 <div class="mt-2 w-50 ml-2 text-left">
92 <div>
93 <small class="text-muted">
94 <translate>Countrycode</translate>
95 </small>
96 <input
97 id="countryCode"
98 type="text"
99 class="form-control"
100 placeholder="AT"
101 aria-label="id"
102 v-model="countryCode"
103 />
104 <span class="text-left text-danger">
105 <small v-if="countryCodeError && !countryCode">
106 <translate>Please enter a countrycode </translate>
107 </small>
108 </span>
109 </div>
110 <div class="w-50 ml-2"></div>
111 </div>
112 </div>
113 <div class="d-flex flex-column justify-content-between">
114 <div class="mt-2 text-left">
115 <small class="text-muted">
116 <translate>Start rhm</translate>
117 </small>
118 <div class="d-flex flex-row">
119 <input
120 id="startrhm"
121 type="text"
122 class="form-control"
123 placeholder="e.g. ATXXX000010000019900"
124 aria-label="startrhm"
125 v-model="startrhm"
126 />
127 <span class="input-group-text" @click="togglePipette('start')">
128 <font-awesome-icon
129 :class="{ 'text-info': pipetteStart }"
130 icon="crosshairs"
131 />
132 </span>
133 </div>
134 <span class="text-left text-danger">
135 <small v-if="startrhmError && !startrhm">
136 <translate>Please enter a start point</translate>
137 </small>
138 </span>
139 </div>
140 <div class="mt-2 text-left">
141 <small class="text-muted"> <translate>End rhm</translate> </small>
142 <div class="d-flex flex-row">
143 <input
144 id="endrhm"
145 type="text"
146 class="form-control"
147 placeholder="e.g. ATXXX000010000019900"
148 aria-label="endrhm"
149 v-model="endrhm"
150 />
151 <span class="input-group-text" @click="togglePipette('end')">
152 <font-awesome-icon
153 :class="{ 'text-info': pipetteEnd }"
154 icon="crosshairs"
155 />
156 </span>
157 </div>
158 <span class="text-left text-danger">
159 <small v-if="endrhmError && !endrhm">
160 <translate>Please enter an end point</translate>
161 </small>
162 </span>
163 </div>
164 </div>
165 <div
166 v-if="!editExistingStretch"
167 class="d-flex flex-row justify-content-between"
168 >
169 <div class="mt-2 mr-2 w-50 text-left">
170 <small class="text-muted">
171 <translate
172 >Tolerance for snapping of waterway axis [m]</translate
173 >
174 </small>
175 <input
176 class="form-control"
177 v-model.number="tolerance"
178 placeholder=""
179 type="number"
180 min="0"
181 step="any"
182 aria-label="tolerance"
183 id="tolerance"
184 />
185 <span class="text-left text-danger">
186 <small v-if="toleranceError && !tolerance">
187 <translate>Please enter a tolerance value</translate>
188 </small>
189 </span>
190 </div>
191 </div>
192 <div class="d-flex flex-row justify-content-between">
193 <div class="mt-2 mr-2 w-50 text-left">
194 <small class="text-muted">
195 <translate>Object name</translate>
196 </small>
197 <input
198 id="objbn"
199 type="text"
200 class="form-control"
201 placeholder=""
202 aria-label="objbn"
203 v-model="objbn"
204 />
205 <span class="text-left text-danger">
206 <small v-if="objbnError && !objbn">
207 <translate>Please enter an objectname</translate>
208 </small>
209 </span>
210 </div>
211 <div class="mt-2 ml-2 w-50 text-left">
212 <small class="text-muted">
213 <translate>National Object name</translate>
214 </small>
215 <input
216 id="nobjbn"
217 type="text"
218 class="form-control"
219 placeholder=""
220 aria-label="nobjbn"
221 v-model="nobjbn"
222 />
223 </div>
224 </div>
225 <div class="d-flex flex-row justify-content-between">
226 <div class="mt-2 mr-2 w-50 text-left">
227 <small class="text-muted">
228 <translate>Date info</translate>
229 </small>
230 <input
231 id="date_info"
232 type="date"
233 class="form-control"
234 placeholder="date_info"
235 aria-label="date_info"
236 v-model="date_info"
237 />
238 <span class="text-left text-danger">
239 <small v-if="date_infoError && !date_info">
240 <translate>Please enter a date</translate>
241 </small>
242 </span>
243 </div>
244 <div class="mt-2 ml-2 w-50 text-left">
245 <small class="text-muted"> <translate>Source</translate> </small>
246 <input
247 id="source"
248 type="text"
249 class="form-control"
250 placeholder="source"
251 aria-label="source"
252 v-model="source"
253 />
254 <span class="text-left text-danger">
255 <small v-if="sourceError && !source">
256 <translate>Please enter a source</translate>
257 </small>
258 </span>
259 </div>
260 </div>
261 </div>
262 <div class="text-right mt-2 mr-3 mb-3">
263 <button @click="edit = false" class="btn btn-warning mr-2">
264 Back
265 </button>
266 <button
267 @click="save"
268 type="submit"
269 class="shadow-sm btn btn-info submit-button"
270 >
271 <translate>Submit</translate>
272 </button>
273 </div>
274 </div>
275 <div class="text-right mr-3">
276 <button v-if="!edit" @click="startEdit()" class="btn btn-info">
277 <translate>New stretch</translate>
278 </button>
279 </div>
280 </div>
281 </div>
282 </template>
283
284 <script>
285 /* This is Free Software under GNU Affero General Public License v >= 3.0
286 * without warranty, see README.md and license for details.
287 *
288 * SPDX-License-Identifier: AGPL-3.0-or-later
289 * License-Filename: LICENSES/AGPL-3.0.txt
290 *
291 * Copyright (C) 2018, 2019 by via donau
292 * – Österreichische Wasserstraßen-Gesellschaft mbH
293 * Software engineering by Intevation GmbH
294 *
295 * Author(s):
296 * Thomas Junk <thomas.junk@intevation.de>
297 * Tom Gottfried <tom.gottfried@intevation.de>
298 */
299 import { mapState, mapGetters } from "vuex";
300 import { displayError, displayInfo } from "@/lib/errors";
301 import { HTTP } from "@/lib/http";
302 import { sortTable } from "@/lib/mixins";
303
304 export default {
305 name: "importstretches",
306 mixins: [sortTable],
307 data() {
308 return {
309 staging: [],
310 edit: false,
311 editExistingStretch: false,
312 id: "",
313 funktion: "",
314 startrhm: "",
315 endrhm: "",
316 tolerance: 5,
317 objbn: "",
318 nobjbn: "",
319 countryCode: "",
320 date_info: new Date().toISOString().split("T")[0],
321 source: "",
322 pipetteStart: false,
323 pipetteEnd: false,
324 idError: false,
325 funktionError: false,
326 startrhmError: false,
327 endrhmError: false,
328 toleranceError: false,
329 objbnError: false,
330 nobjbnError: false,
331 date_infoError: false,
332 sourceError: false,
333 countryCodeError: false,
334 loading: false
335 };
336 },
337 computed: {
338 ...mapState("application", ["searchQuery"]),
339 ...mapState("map", ["identifiedFeatures", "currentMeasurement"]),
340 ...mapGetters("map", ["openLayersMap"]),
341 ...mapGetters("user", ["isSysAdmin"]),
342 ...mapState("imports", ["stretches"]),
343 defineStretchesLabel() {
344 return this.$gettext("Define Stretches");
345 },
346 nameLabel() {
347 return this.$gettext("Name");
348 },
349 dateLabel() {
350 return this.$gettext("Date");
351 },
352 sourceorganizationLabel() {
353 return this.$gettext("Source organization");
354 },
355 stretchesInStaging() {
356 const result = [];
357 for (let stretch of this.stretches) {
358 for (let s of this.staging) {
359 if (s.kind == "st" && s.summary.stretch == stretch.properties.name) {
360 result.push({ name: s.summary.stretch, id: s.id });
361 }
362 }
363 }
364 return result;
365 }
366 },
367 watch: {
368 identifiedFeatures() {
369 const distanceMark = this.identifiedFeatures.find(x =>
370 /^distance_marks_geoserver/.test(x["id_"])
371 );
372 if (distanceMark) {
373 const location = distanceMark.get("location");
374 this.startrhm = this.pipetteStart ? location : this.startrhm;
375 this.endrhm = this.pipetteEnd ? location : this.endrhm;
376 this.pipetteStart = false;
377 this.pipetteEnd = false;
378 this.$store.commit("map/mapPopupEnabled", true);
379 }
380 }
381 },
382 methods: {
383 filteredStretches() {
384 return this.stretches.filter(s => {
385 return (s.properties.name + s.properties.source_organization)
386 .toLowerCase()
387 .includes(this.searchQuery.toLowerCase());
388 });
389 },
390 gotoStaging(id) {
391 this.$router.push("/review/" + id);
392 },
393 isInStaging(stretchname) {
394 for (let s of this.stretchesInStaging) {
395 if (s.name == stretchname) return true;
396 }
397 return false;
398 },
399 getStagingLink(stretchname) {
400 for (let s of this.stretchesInStaging) {
401 if (s.name == stretchname) return s.id;
402 }
403 },
404 loadStagingData() {
405 return new Promise((resolve, reject) => {
406 HTTP.get("/imports?states=pending", {
407 headers: { "X-Gemma-Auth": localStorage.getItem("token") }
408 })
409 .then(response => {
410 const { imports } = response.data;
411 this.staging = imports;
412 resolve(response);
413 })
414 .catch(error => {
415 reject(error);
416 });
417 });
418 },
419 editStretch(stretch) {
420 const properties = stretch.properties;
421 this.date_info = properties.date_info.split("T")[0];
422 this.id = properties.name;
423 this.nobjbn = properties.nobjnam;
424 this.objbn = properties.objnam;
425 this.countryCode = properties.countries;
426 this.source = properties["source_organization"];
427 this.edit = true;
428 this.startrhm = properties.lower;
429 this.endrhm = properties.upper;
430 this.editExistingStretch = true;
431 },
432 deleteStretch(stretch) {
433 this.$store.commit("application/popup", {
434 icon: "trash",
435 title: this.$gettext("Delete Stretch"),
436 content:
437 this.$gettext("Do you really want to delete this stretch:") +
438 `<br>
439 <b>${stretch.properties.name}, ${
440 stretch.properties.source_organization
441 } (${stretch.properties.countries})</b>`,
442 confirm: {
443 label: this.$gettext("Delete"),
444 icon: "trash",
445 callback: () => {
446 displayInfo({
447 title: this.$gettext("Not implemented"),
448 message: this.$gettext("Deleting ") + stretch.id
449 });
450 }
451 },
452 cancel: {
453 label: this.$gettext("Cancel"),
454 icon: "times"
455 }
456 });
457 },
458 moveMapToStretch(stretch) {
459 this.$store.commit("imports/selectedStretchId", stretch.id);
460 this.openLayersMap()
461 .getLayer("STRETCHES")
462 .setVisible(true);
463 this.$store.dispatch("map/moveToFeauture", {
464 feature: stretch,
465 zoom: 17,
466 preventZoomOut: true
467 });
468 },
469 clean() {
470 this.id = "";
471 this.edit = false;
472 this.editExistingStretch = false;
473 this.funktion = "";
474 this.startrhm = "";
475 this.tolerance = 5;
476 this.endrhm = "";
477 this.objbn = "";
478 this.nobjbn = "";
479 this.countryCode = "";
480 this.date_info = new Date().toISOString().split("T")[0];
481 this.source = "";
482 this.pipetteStart = false;
483 this.pipetteEnd = false;
484 this.idError = false;
485 this.funktionError = false;
486 this.startrhmError = false;
487 this.endrhmError = false;
488 this.toleranceError = false;
489 this.objbnError = false;
490 this.nobjbnError = false;
491 this.date_infoError = false;
492 this.sourceError = false;
493 this.countryCodeError = false;
494 },
495 startEdit() {
496 this.clean();
497 this.edit = true;
498 },
499 togglePipette(t) {
500 this.openLayersMap()
501 .getLayer("DISTANCEMARKSAXIS")
502 .setVisible(true);
503 if (t === "start") {
504 this.$store.commit("map/mapPopupEnabled", this.pipetteStart);
505 this.pipetteStart = !this.pipetteStart;
506 this.pipetteEnd = false;
507 } else {
508 this.$store.commit("map/mapPopupEnabled", this.pipetteEnd);
509 this.pipetteEnd = !this.pipetteEnd;
510 this.pipetteStart = false;
511 }
512 },
513 validate() {
514 const fields = [
515 "id",
516 "funktion",
517 "startrhm",
518 "tolerance",
519 "endrhm",
520 "objbn",
521 "nobjbn",
522 "countryCode",
523 "date_info",
524 "source"
525 ];
526 fields.forEach(field => {
527 if (!this[field]) {
528 this[field + "Error"] = true;
529 } else {
530 this[field + "Error"] = false;
531 }
532 });
533 },
534 save() {
535 this.validate();
536 if (
537 !this.id ||
538 !this.startrhm ||
539 !this.endrhm ||
540 (!this.tolerance && this.editExistingStretch) ||
541 !this.source ||
542 !this.date_info ||
543 !this.objbn ||
544 !this.countryCode
545 )
546 return;
547 const data = {
548 name: this.id,
549 from: this.startrhm,
550 to: this.endrhm,
551 "source-organization": this.source,
552 "date-info": this.date_info,
553 objnam: this.objbn,
554 nobjnam: this.nobjbn,
555 countries: this.countryCode.split(",").map(x => {
556 return x.trim();
557 })
558 };
559 if (!this.editExistingStretch) {
560 data["tolerance"] = this.tolerance;
561 }
562 this.$store
563 .dispatch("imports/saveStretch", data)
564 .then(() => {
565 displayInfo({
566 title: this.$gettext("Import"),
567 message: this.$gettext("Starting import of stretch")
568 });
569 this.clean();
570 this.$store.dispatch("imports/loadStretches").then(() => {
571 this.edit = false;
572 });
573 })
574 .catch(error => {
575 const { status, data } = error.response;
576 displayError({
577 title: this.$gettext("Backend Error"),
578 message: `${status}: ${data.message || data}`
579 });
580 });
581 }
582 },
583 mounted() {
584 this.edit = false;
585 this.loading = true;
586 this.$store
587 .dispatch("imports/loadStretches")
588 .catch(error => {
589 const { status, data } = error.response;
590 displayError({
591 title: this.$gettext("Backend Error"),
592 message: `${status}: ${data.message || data}`
593 });
594 })
595 .finally(() => (this.loading = false));
596 this.loadStagingData().catch(error => {
597 const { status, data } = error.response;
598 displayError({
599 title: this.$gettext("Backend Error"),
600 message: `${status}: ${data.message || data}`
601 });
602 });
603 }
604 };
605 </script>