view client/src/components/importoverview/importlogs/LogDetail.vue @ 2442:9b7138751f5b

staging: layout logs
author Thomas Junk <thomas.junk@intevation.de>
date Fri, 01 Mar 2019 12:07:34 +0100
parents 999bb511ef67
children
line wrap: on
line source

<template>
  <div class="entry d-flex flex-column py-1 border-bottom">
    <div class="d-flex flex-row position-relative">
      <small @click="showDetails(job.id)" class="jobid ml-2 mt-1 mr-2">
        {{ job.id }}
      </small>
      <small @click="showDetails(job.id)" class="enqueued mt-1  mr-2">
        {{ formatDateTime(job.enqueued) }}
      </small>
      <small @click="showDetails(job.id)" class="kind mt-1 mr-2">
        {{ job.kind.toUpperCase() }}
      </small>
      <small @click="showDetails(job.id)" class="user mt-1 mr-2">
        {{ job.user }}
      </small>
      <small @click="showDetails(job.id)" class="signer mt-1 mr-2">
        {{ job.signer }}
      </small>
      <small @click="showDetails(job.id)" class="state mt-1 mr-2">
        <span :class="{ 'text-danger': job.state.toUpperCase() == 'FAILED' }"
          >{{ job.state
          }}<font-awesome-icon
            v-if="job.warnings"
            class="ml-1 text-warning"
            icon="exclamation-triangle"
            fixed-width
          ></font-awesome-icon>
        </span>
        <span v-if="!job.warnings" style="margin-right: 1.6em;"></span>
      </small>
      <div @click="showDetails(job.id)" class="mt-1 text-info detailsbutton">
        <font-awesome-icon
          class="pointer"
          v-if="show"
          icon="angle-up"
          fixed-width
        ></font-awesome-icon>
        <font-awesome-icon
          class="pointer"
          v-if="loading"
          icon="spinner"
          fixed-width
        ></font-awesome-icon>
        <font-awesome-icon
          class="pointer"
          v-if="!show && !loading"
          icon="angle-down"
          fixed-width
        ></font-awesome-icon>
      </div>
    </div>
    <div class="detailstable d-flex flex-row">
      <div :class="collapse">
        <div class="text-left">
          <small style="margin-right:10px" class="type condensed"
            ><translate>Kind</translate></small
          >
          <a
            href="#"
            @click="sortAsc = !sortAsc"
            style="margin-right:58px"
            class="datetime sort-link"
            ><small class="condensed"><translate>Date</translate></small>
            <small class="message condensed"
              ><font-awesome-icon
                :icon="sortIcon"
                class="ml-1"
              ></font-awesome-icon></small
          ></a>
          <small class="condensed"><translate>Message</translate></small>
        </div>
        <div class="logentries">
          <div
            v-for="(entry, index) in sortedEntries"
            :key="index"
            class="detailsrow text-left"
          >
            <small
              :class="[
                'condensed type',
                {
                  'text-danger': entry.kind.toUpperCase() == 'ERROR',
                  'text-warning': entry.kind.toUpperCase() == 'WARN'
                }
              ]"
              >{{ entry.kind.toUpperCase() }}</small
            >
            <small
              :class="[
                'condensed datetime',
                {
                  'text-danger': entry.kind.toUpperCase() == 'ERROR',
                  'text-warning': entry.kind.toUpperCase() == 'WARN'
                }
              ]"
              >{{ formatDateTime(entry.time) }}</small
            >
            <small
              :class="[
                'condensed message',
                {
                  'text-danger': entry.kind.toUpperCase() == 'ERROR',
                  'text-warning': entry.kind.toUpperCase() == 'WARN'
                }
              ]"
              >{{ entry.message }}</small
            >
          </div>
        </div>
      </div>
    </div>
  </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):
 * Thomas Junk <thomas.junk@intevation.de>
 */

import { HTTP } from "@/lib/http.js";
import { displayError } from "@/lib/errors.js";
import locale2 from "locale2";

export default {
  name: "importqueuedetail",
  props: ["job", "reload"],
  data() {
    return {
      loading: false,
      show: false,
      entries: [],
      sortAsc: true
    };
  },
  mounted() {
    this.openSpecificDetail();
  },
  watch: {
    $route() {
      this.openSpecificDetail();
    },
    reload() {
      if (this.reload) {
        this.entries = [];
        this.show = false;
      }
    }
  },
  methods: {
    openSpecificDetail() {
      const { id } = this.$route.params;
      if (id == this.job.id) {
        this.showDetails(id);
      } else {
        this.show = false;
      }
    },
    formatDate(date) {
      return date
        ? new Date(date).toLocaleDateString(locale2, {
            day: "2-digit",
            month: "2-digit",
            year: "numeric"
          })
        : "";
    },
    formatDateTime(date) {
      if (!date) return "";
      const d = new Date(date);
      return (
        d.toLocaleDateString(locale2, {
          day: "2-digit",
          month: "2-digit",
          year: "numeric"
        }) +
        " - " +
        d.toLocaleTimeString(locale2, {
          hour12: false
        })
      );
    },
    showDetails(id) {
      if (this.show) {
        this.show = false;
        return;
      }
      if (this.entries.length === 0) {
        this.loading = true;
        HTTP.get("/imports/" + id, {
          headers: { "X-Gemma-Auth": localStorage.getItem("token") }
        })
          .then(response => {
            const { entries } = response.data;
            this.entries = entries;
            this.show = true;
            this.loading = false;
          })
          .catch(error => {
            const { status, data } = error.response;
            displayError({
              title: this.$gettext("Backend Error"),
              message: `${status}: ${data.message || data}`
            });
          });
      } else {
        this.show = true;
      }
    }
  },
  computed: {
    sortedEntries() {
      let sorted = this.entries.slice();
      sorted.sort((r1, r2) => {
        let d1 = new Date(r1.time);
        let d2 = new Date(r2.time);
        if (d2 < d1) {
          return !this.sortAsc ? -1 : 1;
        }
        if (d2 > d1) {
          return !this.sortAsc ? 1 : -1;
        }
        return 0;
      });
      return sorted;
    },
    sortIcon() {
      return this.sortAsc ? "sort-amount-down" : "sort-amount-up";
    },
    icon() {
      return {
        "angle-up": !this.show,
        "angle-down": this.show
      };
    },
    collapse() {
      return {
        details: true,
        collapse: true,
        show: this.show,
        "w-100": true
      };
    }
  }
};
</script>

<style lang="scss" scoped>
.logentries {
  overflow: auto;
}

.condensed {
  font-stretch: condensed;
}

.entry {
  background-color: white;
  cursor: pointer;
  width: 100%;
}

.entry:hover {
  background-color: #efefef;
  transition: 1.6s;
}

.detailstable {
  margin-left: $offset;
  margin-right: $large-offset;
}

.detailsbutton {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
}
.jobid {
  width: 5%;
}

.user {
  width: 15%;
}

.signer {
  width: 15%;
}

.kind {
  width: 10%;
}

.state {
  width: 15%;
}

.details {
  width: 50%;
}

.detailsrow {
  line-height: 0.7rem;
}

.type {
  white-space: nowrap;
}

.datetime {
  white-space: nowrap;
  padding-left: 10px;
  padding-right: 10px;
}

.message {
  white-space: nowrap;
}
</style>