Mercurial > gemma
view client/src/components/Search.vue @ 5560:f2204f91d286
Join the log lines of imports to the log exports to recover data from them.
Used in SR export to extract information that where in the meta json
but now are only found in the log.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Wed, 09 Feb 2022 18:34:40 +0100 |
parents | 1cf9b043dca1 |
children | 84d01a536bec |
line wrap: on
line source
<template> <div :class="searchbarContainerStyle"> <div class="input-group-prepend m-0 d-print-none"> <span @click="toggleSearchbar" :class="searchButtonStyle" for="search"> <font-awesome-icon icon="search" /> </span> </div> <div :class="[ 'searchgroup', { searchgroupwidthbottlenecks: this.showSearchbar && this.contextBoxContent === 'bottlenecks', sgnobottlenecks: this.showSearchbar && this.contextBoxContent !== 'bottlenecks', 'searchgroup-collapsed': !showSearchbar, big: showContextBox && ['bottlenecks', 'staging', 'stretches'].indexOf( contextBoxContent ) !== -1 } ]" > <input @keyup.enter="triggerEnter" id="search" v-model="searchQuery" type="text" :class="searchInputStyle" /> </div> <div v-if="showSearchbar && searchResults !== null && !showContextBox" class="searchresults border-top ui-element bg-white rounded-bottom d-print-none position-absolute" > <div v-for="(entry, index) of searchResults" :key="index" class="border-top text-left" > <a href="#" @click.prevent="moveToSearchResult(entry)" class="p-2 d-block text-nowrap" > <font-awesome-icon icon="ship" v-if="entry.type === 'bottleneck'" class="mr-1" fixed-width /> <font-awesome-icon icon="water" v-if="entry.type === 'rhm'" class="mr-1" fixed-width /> <font-awesome-icon icon="city" v-if="entry.type === 'city'" class="mr-1" fixed-width /> <font-awesome-icon icon="ruler-vertical" v-if="entry.type === 'gauge'" class="mr-1" fixed-width /> <font-awesome-icon icon="road" v-if="['stretch', 'section'].includes(entry.type)" class="mr-1" fixed-width /> {{ entry.name }} <span v-if="entry.location || entry.locationcode" >({{ entry.location }}<span v-if="entry.location && entry.locationcode">, </span >{{ entry.locationcode }})</span > </a> </div> </div> </div> </template> <style lang="scss" scoped> .searchcontainer { opacity: 0.96; } .searchcontainerwitdh { width: 860px; transition: 0.1s; transition-timing-function: ease; } .searchcontainerwitdhbottlenecks { width: 650px; transition: 0.1s; transition-timing-function: ease; } .searchgroupwidth { min-width: 852px; max-width: 860px; transition: 0.1s; transition-timing-function: ease; } .sgnobottlenecks { width: 817px; transition: 0.1s; transition-timing-function: ease; } .searchgroupwidthbottlenecks { width: 617px; transition: 0.1s; transition-timing-function: ease; } .searchcontainer .searchbar { border-top-left-radius: 0 !important; border-bottom-left-radius: 0 !important; } .searchgroup { overflow: hidden; } .searchgroup-collapsed { width: 0; } .searchbar { height: 2rem !important; box-shadow: none !important; } .searchbar.rounded-top-right { border-radius: 0 !important; border-top-right-radius: 0.25rem !important; } .searchlabel.rounded-top-left { border-radius: 0 !important; border-top-left-radius: 0.25rem !important; } .input-group-text { height: 2rem; width: 2rem; } .input-group-prepend svg path { fill: #666; } .searchresults { box-shadow: 0 0.1rem 0.5rem rgba(0, 0, 0, 0.2); top: 2rem; left: 0; right: 0; max-height: 24rem; overflow: auto; } .searchresults > div:first-child { border-top: 0 !important; } .searchresults a { text-decoration: none; } .searchresults a:hover { background: #f8f8f8; } .smallbox { width: 2rem; } </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 by via donau * – Österreichische Wasserstraßen-Gesellschaft mbH * Software engineering by Intevation GmbH * * Author(s): * Markus Kottländer <markus.kottlaender@intevation.de> */ import debounce from "debounce"; import { mapState, mapGetters } from "vuex"; import { displayError } from "@/lib/errors"; import { HTTP } from "@/lib/http"; import { format } from "date-fns"; const setFocus = () => document.querySelector("#search").focus(); export default { name: "search", data() { return { searchQueryIsDirty: false, searchResults: null, isSearching: false }; }, computed: { ...mapState("application", [ "showSearchbar", "showContextBox", "contextBoxContent", "showTimeSlider", "currentVisibleTime" ]), ...mapState("imports", ["startDate", "endDate"]), ...mapGetters("imports", ["filters"]), ...mapGetters("map", ["openLayersMap"]), searchQuery: { get() { return this.$store.state.application.searchQuery; }, set(value) { this.$store.commit("application/searchQuery", value); } }, searchIndicator: function() { if (this.isSearching) { return "⟳"; } else if (this.searchQueryIsDirty) { return ""; } else { return "✓"; } }, searchbarContainerStyle() { return [ "input-group searchcontainer shadow-xs rounded", { "d-flex": this.contextBoxContent !== "imports", "d-none": this.contextBoxContent === "imports" && this.showContextBox, smallbox: !this.showSearchbar, searchcontainerwidth: this.showSearchbar && this.contextBoxContent !== "bottlenecks", searchgroupwidth: this.showSearchbar && this.contextBoxContent !== "bottlenecks", searchcontainerwidthbottlenecks: this.showSearchbar && this.contextBoxContent === "bottlenecks", searchgroupwidthbottleneks: this.showSearchbar && this.contextBoxContent === "bottlenecks" } ]; }, searchInputStyle() { return [ "form-control ui-element search searchbar d-print-none border-0", { "rounded-top-right": this.showContextBox || this.searchResults } ]; }, searchButtonStyle() { return [ "ui-element input-group-text p-0 d-flex border-0 justify-content-center searchlabel bg-white d-print-none", { rounded: !this.showSearchbar, "rounded-left": this.showSearchbar, "rounded-top-left": this.showSearchbar && (this.showContextBox || this.searchResults) } ]; } }, watch: { searchQuery: function() { this.searchQueryIsDirty = true; if (!this.showContextBox) this.triggerSearch(); }, currentVisibleTime() { this.doSearch(); } }, methods: { loadLogs() { this.$store .dispatch("imports/getImports", { filter: this.filters, from: format(this.startDate, "YYYY-MM-DDTHH:mm:ss.SSS"), to: format(this.endDate, "YYYY-MM-DDTHH:mm:ss.SSS"), query: this.searchQuery }) .then(() => {}) .catch(error => { let message = "Backend not reachable"; if (error.response) { const { status, data } = error.response; message = `${status}: ${data.message || data}`; } displayError({ title: this.$gettext("Backend Error"), message: message }); }); }, triggerEnter() { if (this.showContextBox && this.contextBoxContent === "importoverview") { this.loadLogs(); } if (!this.searchResults || this.searchResults.length != 1) return; this.moveToSearchResult(this.searchResults[0]); }, triggerSearch: debounce(function() { this.doSearch(); }, 500), doSearch() { this.isCalculating = true; this.searchResults = null; if (this.searchQuery == "") { return; } HTTP.post( "/search", this.showTimeSlider ? { string: this.searchQuery, time: this.currentVisibleTime.toISOString() } : { string: this.searchQuery }, { headers: { "X-Gemma-Auth": localStorage.getItem("token"), "Content-type": "text/xml; charset=UTF-8" } } ) .then(response => { this.searchResults = response.data; }) .catch(error => { let message = "Backend not reachable"; if (error.response) { const { status, data } = error.response; message = `${status}: ${data.message || data}`; } displayError({ title: this.$gettext("Backend Error"), message: message }); }); this.isCalculating = false; this.searchQueryIsDirty = false; }, moveToSearchResult(resultEntry) { let zoom = 16; if (resultEntry.type === "bottleneck") { this.openLayersMap() .getLayer("BOTTLENECKS") .setVisible(true); this.$store.commit( "bottlenecks/setBottleneckForPrint", resultEntry.name ); } if (resultEntry.type === "rhm") { this.openLayersMap() .getLayer("DISTANCEMARKSAXIS") .setVisible(true); } if (resultEntry.type === "gauge") { this.openLayersMap() .getLayer("GAUGES") .setVisible(true); } if (resultEntry.type === "stretch") { this.$store.commit( "imports/selectedStretchId", "stretches_geoserver." + resultEntry.id ); this.openLayersMap() .getLayer("STRETCHES") .setVisible(true); } if (resultEntry.type === "section") { this.$store.commit( "imports/selectedSectionId", "sections_geoserver." + resultEntry.id ); this.openLayersMap() .getLayer("SECTIONS") .setVisible(true); } if (resultEntry.type === "city") zoom = 13; if (resultEntry.geom.type == "Point") { this.$store.dispatch("map/moveMap", { coordinates: resultEntry.geom.coordinates, zoom, preventZoomOut: true }); } else if (resultEntry.geom.type == "Polygon") { const boundingBox = [ Math.min(...resultEntry.geom.coordinates[0].map(c => c[0])), Math.min(...resultEntry.geom.coordinates[0].map(c => c[1])), Math.max(...resultEntry.geom.coordinates[0].map(c => c[0])), Math.max(...resultEntry.geom.coordinates[0].map(c => c[1])) ]; this.$store.dispatch("map/moveToBoundingBox", { boundingBox, zoom }); } // this.searchQuery = ""; // clear search query again this.toggleSearchbar(); }, toggleSearchbar() { if (!this.showContextBox) { if (!this.showSearchbar) { setTimeout(setFocus, 300); } this.$store.commit("application/showSearchbar", !this.showSearchbar); } } } }; </script>