changeset 2543:a542045f28a6

staging: imports/overview2 alternative implementation
author Thomas Junk <thomas.junk@intevation.de>
date Thu, 07 Mar 2019 16:54:29 +0100
parents fc7d828695c9
children c61339e676e0
files client/src/components/Contextbox.vue client/src/components/importoverview/ImportOverviewAlt.vue client/src/router.js
diffstat 3 files changed, 477 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/client/src/components/Contextbox.vue	Thu Mar 07 15:55:25 2019 +0100
+++ b/client/src/components/Contextbox.vue	Thu Mar 07 16:54:29 2019 +0100
@@ -6,6 +6,9 @@
     <ImportOverview
       v-if="contextBoxContent === 'importoverview'"
     ></ImportOverview>
+    <ImportOverviewAlt
+      v-if="contextBoxContent === 'importoverview2'"
+    ></ImportOverviewAlt>
   </div>
 </template>
 
@@ -31,7 +34,9 @@
     Bottlenecks: () => import("@/components/Bottlenecks"),
     Stretches: () => import("@/components/ImportStretches.vue"),
     ImportOverview: () =>
-      import("@/components/importoverview/ImportOverview.vue")
+      import("@/components/importoverview/ImportOverview.vue"),
+    ImportOverviewAlt: () =>
+      import("@/components/importoverview/ImportOverviewAlt.vue")
   },
   computed: {
     ...mapState("application", [
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/client/src/components/importoverview/ImportOverviewAlt.vue	Thu Mar 07 16:54:29 2019 +0100
@@ -0,0 +1,452 @@
+<template>
+  <div class="overview">
+    <UIBoxHeader
+      icon="clipboard-check"
+      title="Staging Area"
+      :closeCallback="$parent.close"
+    />
+    <div class="imports text-left ml-1 mr-1">
+      <div class="mt-3 d-flex flex-row border-bottom">
+        <div class="icons small condensed">&nbsp;</div>
+        <div class="id small condensed">Id</div>
+        <div class="kind small condensed">Kind</div>
+        <div class="date small condensed">
+          Date
+        </div>
+        <div class="enqueued small condensed">
+          Imported
+        </div>
+        <div class="user small condensed">User</div>
+        <div class="actions small condensed">&nbsp;</div>
+      </div>
+      <div class="d-flex flex-row" v-for="entry in importQueue" :key="entry.id">
+        <div class="d-flex flex-column w-100">
+          <div class="d-flex flex-row">
+            <div class="icons">
+              <font-awesome-icon
+                @click="toggleDetails(entry.id)"
+                class="pointer text-info"
+                v-if="show != entry.id"
+                icon="angle-right"
+                fixed-width
+              ></font-awesome-icon>
+              <font-awesome-icon
+                @click="toggleDetails(entry.id)"
+                class="pointer  text-info"
+                v-if="show == entry.id"
+                icon="angle-down"
+                fixed-width
+              ></font-awesome-icon>
+            </div>
+            <div class="id small condensed">{{ entry.id }}</div>
+            <div class="kind small condensed">
+              {{ entry.kind.toUpperCase() }}
+            </div>
+            <div class="date small condensed">
+              {{ formatSurveyDate(entry.summary.date) }}
+            </div>
+            <div class="enqueued small condensed">
+              {{ formatSurveyDate(entry.enqueued.split("T")[0]) }}
+            </div>
+            <div class="user small condensed">{{ entry.user }}</div>
+            <div class="actions small condensed">
+              <font-awesome-icon
+                v-if="entry.warnings"
+                class="ml-1 text-warning text-info"
+                icon="exclamation-triangle"
+                fixed-width
+              ></font-awesome-icon>
+            </div>
+          </div>
+          <div class="d-flex flex-row" v-if="show == entry.id">
+            <div class="ml-1 details d-flex flex-column">
+              <div
+                v-if="isApprovedGaugeMeasurement(entry.kind.toUpperCase())"
+                class="d-flex flex-row"
+              >
+                <font-awesome-icon
+                  @click="toggleAdditional(entry.id)"
+                  class="pointer text-info"
+                  v-if="showAdditional != entry.id"
+                  icon="angle-right"
+                  fixed-width
+                ></font-awesome-icon>
+                <font-awesome-icon
+                  @click="toggleAdditional(entry.id)"
+                  class="pointer text-info"
+                  v-if="showAdditional == entry.id"
+                  icon="angle-down"
+                  fixed-width
+                ></font-awesome-icon>
+                <small class="condensed">Additional Info</small>
+              </div>
+              <div
+                v-if="showAdditional == entry.id"
+                class="additionalinfo ml-4"
+              >
+                <div v-for="(result, index) in entry.summary" :key="index">
+                  <font-awesome-icon
+                    @click="toggleDiff(index)"
+                    class="pointer text-info"
+                    v-if="showDiff != index"
+                    icon="angle-right"
+                    fixed-width
+                  ></font-awesome-icon>
+                  <font-awesome-icon
+                    @click="toggleDiff(index)"
+                    class="pointer text-info"
+                    v-if="showDiff == index"
+                    icon="angle-down"
+                    fixed-width
+                  ></font-awesome-icon>
+                  <span
+                    v-if="result.versions.length == 1"
+                    class="agmcode text-left"
+                    ><small
+                      >{{ result["fk-gauge-id"] }}
+                      <translate class="mr-1">( New )</translate></small
+                    ></span
+                  >
+                  <span
+                    v-if="result.versions.length == 2"
+                    class="agmcode text-left"
+                    ><small>{{ result["fk-gauge-id"] }}</small></span
+                  >
+                  <span class="agmdetail text-left"
+                    ><small>{{
+                      formatDateTime(result["measure-date"])
+                    }}</small></span
+                  >
+                  <div v-if="showDiff == index" class="ml-1">
+                    <div>
+                      <div class="d-flex flex-row pl-3 text-left">
+                        <div class="header border-bottom agmdetailskeys">
+                          <small><translate>Value</translate></small>
+                        </div>
+                        <div
+                          v-if="result.versions.length == 2"
+                          class="header border-bottom agmdetailsvalues"
+                        >
+                          <small><translate>Old</translate></small>
+                        </div>
+                        <div class="header border-bottom agmdetailsvalues">
+                          <small><translate>New</translate></small>
+                        </div>
+                      </div>
+                      <div
+                        class="d-flex flex-row pl-3 text-left"
+                        v-for="(entry, index) in Object.keys(
+                          result.versions[0]
+                        )"
+                        :key="index"
+                      >
+                        <div
+                          v-if="
+                            result.versions.length == 1 ||
+                              result.versions[0][entry] !=
+                                result.versions[1][entry]
+                          "
+                          class="agmdetailskeys"
+                        >
+                          <small>{{ entry }}</small>
+                        </div>
+                        <div
+                          v-if="
+                            result.versions.length == 1 ||
+                              result.versions[0][entry] !=
+                                result.versions[1][entry]
+                          "
+                          class="agmdetailsvalues"
+                        >
+                          <small>{{ result.versions[0][entry] }}</small>
+                        </div>
+                        <div
+                          v-if="
+                            result.versions.length == 2 &&
+                              result.versions[0][entry] !=
+                                result.versions[1][entry]
+                          "
+                          class="agmdetailsvalues"
+                        >
+                          <small>{{ result.versions[1][entry] }}</small>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+              <div class="d-flex flex-row">
+                <div class="d-flex flex-column">
+                  <div class="d-flex flex-row">
+                    <font-awesome-icon
+                      @click="toggleLogs(entry.id)"
+                      class="pointer text-info"
+                      v-if="showLogs != entry.id"
+                      icon="angle-right"
+                      fixed-width
+                    ></font-awesome-icon>
+                    <font-awesome-icon
+                      @click="toggleLogs(entry.id)"
+                      class="pointer text-info"
+                      v-if="showLogs == entry.id"
+                      icon="angle-down"
+                      fixed-width
+                    ></font-awesome-icon>
+                    <small class="condensed">Logs</small>
+                  </div>
+                  <div v-if="showLogs == entry.id" class="ml-4 logentries">
+                    <div
+                      v-for="(logentry, index) in logEntries"
+                      :key="index"
+                      class="d-flex flex-row"
+                    >
+                      <small
+                        :class="[
+                          'condensed type',
+                          {
+                            'text-danger':
+                              logentry.kind.toUpperCase() == 'ERROR',
+                            'text-warning':
+                              logentry.kind.toUpperCase() == 'WARN'
+                          }
+                        ]"
+                        >{{ logentry.kind.toUpperCase() }}</small
+                      >
+                      <small
+                        :class="[
+                          'condensed type',
+                          {
+                            'text-danger':
+                              logentry.kind.toUpperCase() == 'ERROR',
+                            'text-warning':
+                              logentry.kind.toUpperCase() == 'WARN'
+                          }
+                        ]"
+                        >{{ logentry.time }}</small
+                      >
+                      <small
+                        :class="[
+                          'condensed type',
+                          {
+                            'text-danger':
+                              logentry.kind.toUpperCase() == 'ERROR',
+                            'text-warning':
+                              logentry.kind.toUpperCase() == 'WARN'
+                          }
+                        ]"
+                        >{{ logentry.message }}</small
+                      >
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { displayError } from "@/lib/errors.js";
+import { HTTP } from "@/lib/http";
+import { formatSurveyDate, formatDateTime } from "@/lib/date.js";
+
+//import { mapState } from "vuex";
+
+const NODETAILS = -1;
+const NODIFF = -1;
+
+export default {
+  name: "importoverviewalt",
+  data() {
+    return {
+      showDiff: NODIFF,
+      importQueue: [],
+      logEntries: [],
+      show: NODETAILS,
+      showAdditional: NODETAILS,
+      showLogs: NODETAILS
+    };
+  },
+  methods: {
+    isFairwayDimension(kind) {
+      return kind === "FD";
+    },
+    isApprovedGaugeMeasurement(kind) {
+      return kind === "AGM";
+    },
+    isBottleneck(kind) {
+      return kind === "BN" || kind === "UBN";
+    },
+    isStretch(kind) {
+      return kind === "ST";
+    },
+    toggleAdditional(id) {
+      if (id === this.showAdditional) {
+        this.showAdditional = NODETAILS;
+        return;
+      }
+      this.showAdditional = id;
+    },
+    toggleDiff(id) {
+      if (id === this.showDiff) {
+        this.showDiff = NODIFF;
+        return;
+      }
+      this.showDiff = id;
+    },
+    toggleLogs(id) {
+      if (id === this.showLogs) {
+        this.showLogs = NODETAILS;
+        return;
+      }
+      this.loadLogEntries(id);
+    },
+    toggleDetails(id) {
+      if (id === this.show) {
+        this.show = NODETAILS;
+        this.showAdditional = NODETAILS;
+        this.showLogs = NODETAILS;
+        return;
+      }
+      this.show = id;
+    },
+    refresh() {
+      this.reload = true;
+      this.loadImportQueue();
+      this.loadLogs();
+    },
+    formatSurveyDate(date) {
+      return formatSurveyDate(date);
+    },
+    formatDateTime(date) {
+      return formatDateTime(date);
+    },
+    loadLogEntries(id) {
+      HTTP.get("/imports/" + id, {
+        headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+      })
+        .then(response => {
+          const { entries } = response.data;
+          this.logEntries = entries;
+          this.showLogs = id;
+        })
+        .catch(error => {
+          const { status, data } = error.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status}: ${data.message || data}`
+          });
+        });
+    },
+    loadImportQueue() {
+      HTTP.get("/imports?states=running,pending", {
+        headers: { "X-Gemma-Auth": localStorage.getItem("token") }
+      })
+        .then(response => {
+          this.importQueue = response.data.imports;
+        })
+        .catch(error => {
+          const { status, data } = error.response;
+          displayError({
+            title: "Backend Error",
+            message: `${status}: ${data.message || data}`
+          });
+        });
+    },
+    loadLogs() {
+      this.$store
+        .dispatch("imports/getImports")
+        .then(() => {
+          this.reload = false;
+        })
+        .catch(error => {
+          const { status, data } = error.response;
+          displayError({
+            title: this.$gettext("Backend Error"),
+            message: `${status}: ${data.message || data}`
+          });
+        });
+    }
+  },
+  mounted() {
+    this.refresh();
+  },
+  NODETAILS: NODETAILS
+};
+</script>
+
+<style lang="scss" scoped>
+.agmdetailskeys {
+  width: 100px;
+}
+
+.agmdetailsvalues {
+  width: 200px;
+}
+
+.overview {
+  max-height: 800px;
+}
+.logentries {
+  max-height: 400px;
+  max-width: 600px;
+  font-size: smaller;
+  white-space: nowrap;
+  line-height: 1.1em;
+  overflow: auto;
+}
+
+.additionalinfo {
+  max-width: 650px;
+  max-height: 200px;
+  overflow-y: auto;
+}
+
+.additionalinfo > div:hover {
+  background: #eee;
+}
+
+.logentries > div:hover {
+  background: #eee;
+}
+
+.details {
+  width: 100%;
+  padding: 5px;
+  cursor: pointer;
+}
+
+.imports {
+  line-height: 0.8rem;
+}
+
+.icons {
+  width: 4%;
+}
+
+.id {
+  width: 10%;
+}
+
+.kind {
+  width: 10%;
+}
+
+.date {
+  width: 20%;
+}
+
+.enqueued {
+  width: 20%;
+}
+
+.user {
+  width: 10%;
+}
+.actions {
+  width: 16%;
+}
+</style>
--- a/client/src/router.js	Thu Mar 07 15:55:25 2019 +0100
+++ b/client/src/router.js	Thu Mar 07 16:54:29 2019 +0100
@@ -194,6 +194,25 @@
       }
     },
     {
+      path: "/imports/overview2/:id?",
+      name: "importoverview2",
+      component: Main,
+      meta: {
+        requiresAuth: true
+      },
+      beforeEnter: (to, from, next) => {
+        const isWaterwayAdmin = store.getters["user/isWaterwayAdmin"];
+        if (!isWaterwayAdmin) {
+          next("/");
+        } else {
+          store.commit("application/showContextBox", true);
+          store.commit("application/contextBoxContent", "importoverview2");
+          store.commit("application/showSearchbar", true);
+          next();
+        }
+      }
+    },
+    {
       path: "/stretches",
       name: "stretches",
       component: Main,