changeset 1230:957907eaaa72

implemented context sensitive box below search bar (see: issue224)
author Markus Kottlaender <markus@intevation.de>
date Tue, 20 Nov 2018 08:47:53 +0100
parents d395b2940a82
children 315e618a29fd
files client/src/App.vue client/src/application/Contextbox.vue client/src/application/Search.vue client/src/application/Sidebar.vue client/src/bottlenecks/Bottlenecks.vue client/src/imports/Imports.vue client/src/staging/Staging.vue client/src/store/application.js
diffstat 8 files changed, 199 insertions(+), 247 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/App.vue	Tue Nov 20 00:51:33 2018 +0100
+++ b/client/src/App.vue	Tue Nov 20 08:47:53 2018 +0100
@@ -6,9 +6,7 @@
                     <Sidebar :routeName="routeName"></Sidebar>
                     <div class="d-flex flex-column" style="max-width: 600px;">
                         <Search v-if="routeName == 'mainview'"></Search>
-                        <Bottlenecks v-if="routeName == 'mainview'"></Bottlenecks>
-                        <Imports v-if="routeName == 'mainview'"></Imports>
-                        <Staging v-if="routeName == 'mainview'"></Staging>
+                        <Contextbox v-if="routeName == 'mainview'"></Contextbox>
                     </div>
                 </div>
                 <div class="ml-auto d-flex">
@@ -90,9 +88,7 @@
     Layers: () => import("./layers/Layers"),
     Sidebar: () => import("./application/Sidebar"),
     Search: () => import("./application/Search"),
-    Bottlenecks: () => import("./bottlenecks/Bottlenecks"),
-    Imports: () => import("./imports/Imports.vue"),
-    Staging: () => import("./staging/Staging.vue")
+    Contextbox: () => import("./application/Contextbox")
   }
 };
 </script>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/application/Contextbox.vue	Tue Nov 20 08:47:53 2018 +0100
@@ -0,0 +1,88 @@
+<template>
+    <div :class="style">
+        <div @click="close" class="ui-element close-contextbox">
+            <i class="fa fa-close"></i>
+        </div>
+        <Bottlenecks v-if="showInContextBox === 'bottlenecks'"></Bottlenecks>
+        <Imports v-if="showInContextBox === 'imports'"></Imports>
+        <Staging v-if="showInContextBox === 'staging'"></Staging>
+    </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 by via donau
+ *   – Österreichische Wasserstraßen-Gesellschaft mbH
+ * Software engineering by Intevation GmbH
+ *
+ * Author(s):
+ * Markus Kottländer <markus.kottlaender@intevation.de>
+ */
+import { mapState } from "vuex";
+
+export default {
+  name: "contextbox",
+  components: {
+    Bottlenecks: () => import("../bottlenecks/Bottlenecks"),
+    Imports: () => import("../imports/Imports.vue"),
+    Staging: () => import("../staging/Staging.vue")
+  },
+  computed: {
+    ...mapState("application", ["showSearchbarLastState", "showInContextBox"]),
+    style() {
+      return [
+        "ui-element shadow contextbox pt-3 ml-3 rounded-bottom border-top",
+        {
+          contextboxcollapsed: !this.showInContextBox,
+          contextboxextended: this.showInContextBox
+        }
+      ];
+    }
+  },
+  methods: {
+    close() {
+      this.$store.commit("application/showInContextBox", null);
+      this.$store.commit(
+        "application/showSearchbar",
+        this.showSearchbarLastState
+      );
+    }
+  }
+};
+</script>
+
+<style lang="sass" scoped>
+.contextbox
+  position: relative
+  background-color: #ffffff
+  opacity: $slight-transparent
+  transition: left 0.3s ease
+  overflow: hidden
+  background: #fff
+
+.contextboxcollapsed
+  width: 0
+  height: 0
+  transition: $transition-fast
+
+.contextboxextended
+  min-width: 600px
+
+.close-contextbox
+  position: absolute
+  z-index: 2
+  right: 0
+  top: 7px
+  height: $icon-width
+  width: $icon-height
+  display: none
+
+.contextboxextended .close-contextbox
+  display: block
+</style>
--- a/client/src/application/Search.vue	Tue Nov 20 00:51:33 2018 +0100
+++ b/client/src/application/Search.vue	Tue Nov 20 08:47:53 2018 +0100
@@ -14,7 +14,7 @@
                 type="text"
                 :class="searchInputStyle"
             >
-            <ul v-if="showSearchbar && searchResults !== null && !showBottlenecks" class="list-group d-print-none">
+            <ul v-if="showSearchbar && searchResults !== null && !showInContextBox" class="list-group d-print-none">
                 <li v-for="entry of searchResults" :key="entry.name" class="list-group-item">
                     <a href="#" @click.prevent="moveToSearchResult(entry)">{{entry.name}}</a>
                 </li>
@@ -92,11 +92,7 @@
     };
   },
   computed: {
-    ...mapState("application", [
-      "showSearchbar",
-      "showBottlenecks",
-      "showImportSoundingResults"
-    ]),
+    ...mapState("application", ["showSearchbar", "showInContextBox"]),
     searchQuery: {
       get() {
         return this.$store.state.application.searchQuery;
@@ -120,15 +116,15 @@
         {
           "searchbar-collapsed": !this.showSearchbar,
           "searchbar-expanded": this.showSearchbar,
-          "d-flex": !this.showImportSoundingResults,
-          "d-none": this.showImportSoundingResults
+          "d-flex": this.showInContextBox !== "imports",
+          "d-none": this.showInContextBox === "imports"
         }
       ];
     },
     searchInputStyle() {
       return [
         "form-control ui-element search searchbar d-print-none border-0",
-        { "rounded-top-right": this.showSearchbar && this.showBottlenecks }
+        { "rounded-top-right": this.showInContextBox }
       ];
     },
     searchButtonStyle() {
@@ -137,7 +133,7 @@
         {
           rounded: !this.showSearchbar,
           "rounded-left": this.showSearchbar,
-          "rounded-top-left": this.showSearchbar && this.showBottlenecks
+          "rounded-top-left": this.showSearchbar && this.showInContextBox
         }
       ];
     }
@@ -207,7 +203,7 @@
       this.toggleSearchbar();
     },
     toggleSearchbar() {
-      if (!this.showBottlenecks) {
+      if (!this.showInContextBox) {
         if (!this.showSearchbar) {
           setTimeout(setFocus, 300);
         }
--- a/client/src/application/Sidebar.vue	Tue Nov 20 00:51:33 2018 +0100
+++ b/client/src/application/Sidebar.vue	Tue Nov 20 08:47:53 2018 +0100
@@ -17,7 +17,7 @@
                         class="text-body d-flex flex-row"
                         v-if="routeName == 'mainview'"
                         href="#"
-                        @click="toggleBottlenecks"
+                        @click="toggleContextBox('bottlenecks')"
                     >Bottlenecks</a>
                 </div>
                 <div v-if="isSysAdmin">
@@ -30,7 +30,7 @@
                         <a
                             href="#"
                             class="text-body"
-                            @click="toggleImportSoundingResults"
+                            @click="toggleContextBox('imports')"
                         >Import soundingresults</a>
                     </div>
                     <div v-if="routeName == 'mainview'" class="d-flex flex-row nav-link">
@@ -38,7 +38,7 @@
                         <a
                             href="#"
                             class="text-body"
-                            @click="$store.commit('application/showStagingArea', !showStagingArea);"
+                            @click="toggleContextBox('staging')"
                         >Staging area</a>
                     </div>
                     <router-link class="text-body d-flex flex-row nav-link" to="usermanagement">
@@ -96,10 +96,8 @@
     ...mapState("user", ["user"]),
     ...mapState("application", [
       "showSidebar",
-      "showBottlenecks",
-      "showImportSoundingResults",
-      "showStagingArea",
-      "showSearchbarLastState"
+      "showSearchbarLastState",
+      "showInContextBox"
     ]),
     menuStyle() {
       return {
@@ -126,10 +124,12 @@
       this.$store.commit("application/showSplitscreen", false);
       this.$router.push("/login");
     },
-    toggleBottlenecks() {
-      this.$store.commit("application/showBottlenecks", !this.showBottlenecks);
-      this.$store.commit("application/showImportSoundingResults", false);
-      if (this.showBottlenecks) {
+    toggleContextBox(context) {
+      this.$store.commit(
+        "application/showInContextBox",
+        this.showInContextBox === context ? null : context
+      );
+      if (this.showInContextBox === context) {
         this.$store.commit("application/showSearchbar", true);
       } else {
         this.$store.commit(
@@ -137,13 +137,6 @@
           this.showSearchbarLastState
         );
       }
-    },
-    toggleImportSoundingResults() {
-      this.$store.commit(
-        "application/showImportSoundingResults",
-        !this.showImportSoundingResults
-      );
-      this.$store.commit("application/showBottlenecks", false);
     }
   }
 };
--- a/client/src/bottlenecks/Bottlenecks.vue	Tue Nov 20 00:51:33 2018 +0100
+++ b/client/src/bottlenecks/Bottlenecks.vue	Tue Nov 20 08:47:53 2018 +0100
@@ -1,8 +1,5 @@
 <template>
-    <div :class="bottlenecksStyle">
-        <div @click="closeBottlenecks" class="ui-element close-bottlenecks">
-            <i class="fa fa-close"></i>
-        </div>
+    <div>
         <h4>Bottlenecks</h4>
         <hr class="mb-0">
         <div class="row p-2 text-left small">
@@ -96,23 +93,8 @@
     };
   },
   computed: {
-    ...mapState("application", [
-      "showBottlenecks",
-      "showSidebar",
-      "searchQuery",
-      "showSearchbarLastState"
-    ]),
+    ...mapState("application", ["searchQuery", "showSearchbarLastState"]),
     ...mapState("bottlenecks", ["bottlenecks"]),
-    ...mapState("map", ["openLayersMap"]),
-    bottlenecksStyle() {
-      return [
-        "ui-element shadow bottlenecks pt-3 ml-3 rounded-bottom border-top",
-        {
-          bottleneckscollapsed: !this.showBottlenecks,
-          bottlenecksextended: this.showBottlenecks
-        }
-      ];
-    },
     sortClass() {
       return [
         "fa ml-1",
@@ -214,13 +196,6 @@
           });
       }
     },
-    closeBottlenecks() {
-      this.$store.commit("application/showBottlenecks", false);
-      this.$store.commit(
-        "application/showSearchbar",
-        this.showSearchbarLastState
-      );
-    },
     displayCurrentSurvey(current) {
       return current ? current.substr(0, current.length - 1) : "";
     },
@@ -235,34 +210,6 @@
 </script>
 
 <style lang="sass" scoped>
-.bottlenecks
-  position: relative
-  background-color: #ffffff
-  opacity: $slight-transparent
-  transition: left 0.3s ease
-  overflow: hidden
-  background: #fff
-
-.bottleneckscollapsed
-  width: 0
-  height: 0
-  transition: $transition-fast
-
-.bottlenecksextended
-  min-width: 600px
-
-.close-bottlenecks
-  position: absolute
-  z-index: 2
-  right: 0
-  top: 7px
-  height: $icon-width
-  width: $icon-height
-  display: none
-
-.bottlenecksextended .close-bottlenecks
-  display: block
-
 .bottleneck-list
   overflow-y: auto
   max-height: 500px
--- a/client/src/imports/Imports.vue	Tue Nov 20 00:51:33 2018 +0100
+++ b/client/src/imports/Imports.vue	Tue Nov 20 08:47:53 2018 +0100
@@ -1,11 +1,5 @@
 <template>
-    <div :class="importStyle">
-        <div
-            @click="$store.commit('application/showImportSoundingResults', false);"
-            class="ui-element close-imports"
-        >
-            <i class="fa fa-close"></i>
-        </div>
+    <div>
         <h4>Import soundingresults</h4>
         <hr class="mr-auto ml-auto mb-0 w-90">
         <div v-if="editState" class="p-3">
@@ -82,7 +76,6 @@
 <script>
 import { HTTP } from "../application/lib/http";
 import { displayError, displayInfo } from "../application/lib/errors.js";
-import { mapState } from "vuex";
 
 const defaultLabel = "Choose .zip-file";
 const IMPORTSTATE = { UPLOAD: "UPLOAD", EDIT: "EDIT" };
@@ -132,22 +125,12 @@
     }
   },
   computed: {
-    ...mapState("application", ["showImportSoundingResults"]),
     editState() {
       return this.importState === IMPORTSTATE.EDIT;
     },
     uploadState() {
       return this.importState === IMPORTSTATE.UPLOAD;
     },
-    importStyle() {
-      return [
-        "ui-element shadow imports ml-3 pt-3 rounded",
-        {
-          importscollapsed: !this.showImportSoundingResults,
-          importsextended: this.showImportSoundingResults
-        }
-      ];
-    },
     dataLink() {
       return (
         "data:text/json;charset=utf-8," +
@@ -191,36 +174,6 @@
 .uploadsection
   width: 90%
 
-.imports
-  position: relative
-  background-color: #ffffff
-  opacity: $slight-transparent
-  transition: left 0.3s ease
-  overflow: hidden
-  background: #fff
-  margin-top: -$offset
-  width: 90%
-
-.importscollapsed
-  width: 0
-  height: 0
-  transition: $transition-fast
-
-.importsextended
-  min-width: 600px
-
-.close-imports
-  position: absolute
-  z-index: 2
-  right: 0
-  top: 7px
-  height: $icon-width
-  width: $icon-height
-  display: none
-
-.importsextended .close-imports
-  display: block
-
 .label-text
   width: 10rem
   text-align: left
--- a/client/src/staging/Staging.vue	Tue Nov 20 00:51:33 2018 +0100
+++ b/client/src/staging/Staging.vue	Tue Nov 20 08:47:53 2018 +0100
@@ -1,55 +1,43 @@
 <template>
-    <div v-if="showStagingArea" :class="stagingAreaStyle">
-        <div class="p-3">
-            <div
-                @click="$store.commit('application/showStagingArea', !showStagingArea);"
-                class="ui-element close-showStagingArea position-absolute"
-            >
-                <i class="fa fa-close"></i>
-            </div>
-            <div class="card-title mb-4 headline">
-                <h4>Staging area</h4>
-                <hr>
-            </div>
-            <div class="d-flex flex-row input-group mb-5">
-                <table class="table mb-4">
-                    <thead>
-                        <tr>
-                            <th>Name</th>
-                            <th>Datatype</th>
-                            <th>Importdate</th>
-                            <th>ImportID</th>
-                            <th>&nbsp;</th>
-                            <th>&nbsp;</th>
-                        </tr>
-                    </thead>
-                    <tbody>
-                        <tr v-for="data in filteredData" :key="data.id">
-                            <td>
-                                <a @click="zoomTo(data.location)" href="#">{{data.name}}</a>
-                            </td>
-                            <td>{{data.type}}</td>
-                            <td>{{data.date}}</td>
-                            <td>{{data.importID}}</td>
-                            <td>
-                                <button class="btn btn-outline-info">
-                                    <i class="fa fa-thumbs-o-up"></i>
-                                </button>
-                            </td>
-                            <td>
-                                <button class="btn btn-outline-info">
-                                    <i class="fa fa-thumbs-o-down"></i>
-                                </button>
-                            </td>
-                        </tr>
-                    </tbody>
-                </table>
-            </div>
-            <div class="confirmbutton">
-                <button class="btn btn-info">Confirm</button>
-            </div>
-        </div>
-    </div>
+  <div>
+      <h4>Staging area</h4>
+      <hr>
+      <table class="table mb-0">
+          <thead>
+              <tr>
+                  <th>Name</th>
+                  <th>Location</th>
+                  <th>Datatype</th>
+                  <th>Importdate</th>
+                  <th>ImportID</th>
+                  <th>&nbsp;</th>
+              </tr>
+          </thead>
+          <tbody>
+              <tr v-for="data in filteredData" :key="data.id">
+                  <td>{{ data.name }}</td>
+                  <td>
+                      <a
+                          @click="zoomTo(data.location)"
+                          href="#"
+                      >{{ data.location[0] }}, {{ data.location[1] }}</a>
+                  </td>
+                  <td>{{ data.type }}</td>
+                  <td>{{ data.date }}</td>
+                  <td>{{ data.importID }}</td>
+                  <td>
+                      <input
+                          type="checkbox"
+                          aria-label="Checkbox for following text input"
+                      >
+                  </td>
+              </tr>
+          </tbody>
+      </table>
+      <div class="p-3">
+          <button class="btn btn-info">Confirm</button>
+      </div>
+   </div>
 </template>
 
 <script>
@@ -105,60 +93,59 @@
     }
   },
   computed: {
-    ...mapState("application", ["showStagingArea"]),
+    ...mapState("application", ["searchQuery"]),
     filteredData() {
-      return demodata;
-    },
-    stagingAreaStyle() {
-      return [
-        "ui-element staging-card bg-white ml-3 pt-3 mx-auto rounded border-top position-relative",
-        {
-          stagingcollapsed: !this.showStagingArea,
-          stagingextended: this.showStagingArea
-        }
-      ];
+      return demodata.filter(data => {
+        const nameFound = data.name
+          .toLowerCase()
+          .includes(this.searchQuery.toLowerCase());
+        const dateFound = data.date
+          .toLowerCase()
+          .includes(this.searchQuery.toLowerCase());
+        const locationFound = data.location.find(coord => {
+          return coord
+            .toString()
+            .toLowerCase()
+            .includes(this.searchQuery.toLowerCase());
+        })
+        const statusFound = data.status
+          .toLowerCase()
+          .includes(this.searchQuery.toLowerCase());
+        const importIDFound = data.importID
+          .toLowerCase()
+          .includes(this.searchQuery.toLowerCase());
+        const typeFound = data.type
+          .toLowerCase()
+          .includes(this.searchQuery.toLowerCase());
+
+        return (
+          nameFound ||
+          dateFound ||
+          locationFound ||
+          statusFound ||
+          importIDFound ||
+          typeFound
+        );
+      });
     }
-  }
+  },
+  methods: {
+    zoomTo(coordinates) {
+      this.$store.commit("map/moveMap", {
+        coordinates: coordinates,
+        zoom: 17,
+        preventZoomOut: true
+      });
+    }
+  },
 };
 </script>
 
 <style lang="sass" scoped>
-.close-showStagingArea
-  z-index: 2
-  right: 0
-  top: 7px
-  height: $icon-width
-  width: $icon-height
-
-.stagingcollapsed
-  width: 0
-  height: 0
-  transition: $transition-fast
-
-.stagingextended
-  min-width: 600px
-
-.staging-card
-  position: relative
-  background-color: #ffffff
-  padding-top: $offset
-  opacity: $slight-transparent
-  transition: left 0.3s ease
-  overflow: hidden
-  background: #fff
-  margin-left: $offset !important
-  margin-top: -$offset !important
-  width: 90%
-
 .table th,
 td
   font-size: 0.9rem
   border-top: 0px !important
   text-align: left
   padding: 0.5rem !important
-
-.confirmbutton
-  position: absolute
-  bottom: $offset
-  right: $large-offset
 </style>
--- a/client/src/store/application.js	Tue Nov 20 00:51:33 2018 +0100
+++ b/client/src/store/application.js	Tue Nov 20 08:47:53 2018 +0100
@@ -23,15 +23,13 @@
     secondaryLogo: process.env.VUE_APP_SECONDARY_LOGO_URL,
     showSidebar: false,
     showUsermenu: false,
-    showBottlenecks: false,
     showSplitscreen: false,
     showSearchbar: false,
     showSearchbarLastState: false,
     showIdentify: false,
     showLayers: true,
     showPdfTool: false,
-    showImportSoundingResults: false,
-    showStagingArea: false,
+    showInContextBox: null, // bottlenecks, imports, staging
     countries: ["AT", "SK", "HU", "HR", "RS", "BiH", "BG", "RO", "UA"],
     searchQuery: "",
     version
@@ -57,12 +55,6 @@
     showSidebar: (state, show) => {
       state.showSidebar = show;
     },
-    showBottlenecks: (state, show) => {
-      state.showBottlenecks = show;
-      if (show) {
-        state.showSearchbarLastState = state.showSearchbar;
-      }
-    },
     showSplitscreen: (state, show) => {
       state.showSplitscreen = show;
     },
@@ -81,11 +73,11 @@
     showPdfTool: (state, show) => {
       state.showPdfTool = show;
     },
-    showImportSoundingResults: (state, show) => {
-      state.showImportSoundingResults = show;
-    },
-    showStagingArea: (state, show) => {
-      state.showStagingArea = show;
+    showInContextBox: (state, context) => {
+      state.showInContextBox = context;
+      if (context) {
+        state.showSearchbarLastState = state.showSearchbar;
+      }
     },
     searchQuery: (state, searchQuery) => {
       state.searchQuery = searchQuery;