view client/src/components/systemconfiguration/PDFTemplates.vue @ 5095:e21cbb9768a2

Prevent duplicate fairway areas In principal, there can be only one or no fairway area at each point on the map. Since polygons from real data will often be topologically inexact, just disallow equal geometries. This will also help to avoid importing duplicates with concurrent imports, once the history of fairway dimensions will be preserved.
author Tom Gottfried <tom@intevation.de>
date Wed, 25 Mar 2020 18:10:02 +0100
parents 6b054b91d9b2
children 7768f14f6535
line wrap: on
line source

<template>
  <div class="d-flex flex-column pb-4">
    <input
      @change="uploadTemplate"
      id="uploadTemplate"
      ref="uploadTemplate"
      type="file"
      style="visibility: hidden; position: absolute;"
    />
    <UITableHeader
      :columns="[
        { id: 'name', title: `${nameLabel}`, class: 'col-3' },
        { id: 'time', title: `${dateLabel}`, class: 'col-3' },
        { id: 'type', title: `${typeLabel}`, class: 'col-2' },
        { id: 'country', title: `${countryLabel}`, class: 'col-2' }
      ]"
    />
    <UITableBody :data="templates | sortTable(sortColumn, sortDirection)">
      <template v-slot:row="{ item: template }">
        <div class="py-1 col-3">{{ template.name }}</div>
        <div class="py-1 col-3">{{ template.time }}</div>
        <div class="py-1 col-2">{{ template.type }}</div>
        <div class="py-1 col-2" v-if="template.country">
          {{ template.country }}
        </div>
        <div class="py-1 col-2" v-else><i>global</i></div>
        <div class="col py-1 text-right">
          <button
            class="btn btn-xs btn-info mr-1"
            ref="downloadTemplate"
            @click="downloadTemplate(template)"
          >
            <font-awesome-icon icon="download" fixed-width />
          </button>
          <button class="btn btn-xs btn-dark" @click="deleteTemplate(template)">
            <font-awesome-icon icon="trash" fixed-width />
          </button>
        </div>
      </template>
    </UITableBody>
    <div class="p-3 border-top">
      <button
        class="btn btn-info btn-sm mr-2"
        @click="
          type = 'map';
          $refs.uploadTemplate.click();
        "
      >
        <font-awesome-icon
          icon="spinner"
          class="fa-spin fa-fw"
          v-if="uploading"
        />
        <font-awesome-icon icon="upload" class="fa-fw" v-else />
        <translate>Upload new map template</translate>
      </button>
      <button
        class="btn btn-info btn-sm"
        @click="
          type = 'diagram';
          $refs.uploadTemplate.click();
        "
      >
        <font-awesome-icon
          icon="spinner"
          class="fa-spin fa-fw"
          v-if="uploading"
        />
        <font-awesome-icon icon="upload" class="fa-fw" v-else />
        <translate>Upload new diagram template</translate>
      </button>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.table th,
td {
  font-size: $smaller;
  border-top: 0px !important;
  text-align: left;
  padding: $small-offset !important;
}
</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, 2019 by via donau
 *   – Österreichische Wasserstraßen-Gesellschaft mbH
 * Software engineering by Intevation GmbH
 *
 * Author(s):
 * Markus Kottländer <markus@intevation.de>
 * Fadi Abbud <fadi.abbud@intevation.de>
 */
import { HTTP } from "@/lib/http";
import { displayError, displayInfo } from "@/lib/errors";
import { sortTable } from "@/lib/mixins";

export default {
  name: "pdftemplates",
  mixins: [sortTable],
  data() {
    return {
      templates: [],
      uploading: false,
      type: null
    };
  },
  computed: {
    nameLabel() {
      return this.$gettext("Name");
    },
    dateLabel() {
      return this.$gettext("Date");
    },
    countryLabel() {
      return this.$gettext("Country");
    },
    typeLabel() {
      return this.$gettext("Type");
    }
  },
  methods: {
    downloadTemplate(template) {
      if (template) {
        var templateData = "";
        var element = document.createElement("a");
        element.style.display = "none";
        element.setAttribute("download", template.name + ".json");
        document.body.appendChild(element);
        HTTP.get(
          `/templates/${template.type}/${encodeURIComponent(template.name)}`,
          {
            headers: {
              "X-Gemma-Auth": localStorage.getItem("token"),
              "Content-type": "text/xml; charset=UTF-8"
            }
          }
        )
          .then(response => {
            templateData = response.data.template_data;
            element.setAttribute(
              "href",
              "data:text/json;charset=utf-8," +
                encodeURIComponent(
                  JSON.stringify(
                    {
                      name: templateData.name,
                      properties: templateData.properties,
                      elements: templateData.elements
                    },
                    null,
                    2
                  )
                )
            );
            element.click();
          })
          .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
            });
          })
          .finally(() => {
            document.body.removeChild(element);
          });
      }
    },
    uploadTemplate() {
      const reader = new FileReader();
      reader.onload = event => {
        let template;
        try {
          template = JSON.parse(event.target.result);
        } catch (e) {
          displayError({
            title: this.$gettext("Format Error"),
            message: this.$gettext(
              "Uploaded file does not contain valid json data."
            )
          });
          // allow the user to upload the same file
          // if user wants to upload the same file after edit it.
          this.$refs.uploadTemplate.value = null;
        }
        if (template.name) {
          // check if an element in the uploaded file does not match the predefind template-elements
          let checkElement = false;
          template.elements.forEach(e => {
            if (
              [
                "text",
                "box",
                "textbox",
                "image",
                "bottleneck",
                "legend",
                "scalebar",
                "scale",
                "northarrow",
                "diagramlegend",
                "diagramtitle",
                "diagram"
              ].indexOf(e.type) === -1
            ) {
              checkElement = true;
              displayError({
                title: this.$gettext("Invalid element"),
                message:
                  e.type +
                  this.$gettext(" does not match any template's element")
              });
              // allow the user to upload the same file
              this.$refs.uploadTemplate.value = null;
            }
          });

          if (!checkElement) {
            this.uploading = true;
            HTTP.post(
              "/templates/" + this.type + "/" + template.name,
              {
                template_name: template.name,
                template_data: template
              },
              {
                headers: {
                  "X-Gemma-Auth": localStorage.getItem("token"),
                  "Content-type": "text/xml; charset=UTF-8"
                }
              }
            )
              .then(() => {
                this.loadTemplates();
                displayInfo({
                  message:
                    template.name + " " + this.$gettext("uploaded successfully")
                });
              })
              .catch(e => {
                const { status, data } = e.response;
                if (status === 400) {
                  displayError({
                    title: this.$gettext("Error"),
                    message: `${data.message || data}`
                  });
                } else {
                  displayError({
                    title: this.$gettext("Backend Error"),
                    message: `${status}: ${data.message || data}`
                  });
                }
              })
              .finally(() => {
                this.uploading = false;
                this.$refs.uploadTemplate.value = null;
              });
          }
        } else {
          displayError({
            title: this.$gettext("Format Error"),
            message: this.$gettext(
              "The provided template has no name property."
            )
          });
          // allow the user to upload the same file
          this.$refs.uploadTemplate.value = null;
        }
      };
      reader.readAsText(this.$refs.uploadTemplate.files[0]);
    },
    loadTemplates() {
      HTTP.get("/templates", {
        headers: {
          "X-Gemma-Auth": localStorage.getItem("token"),
          "Content-type": "text/xml; charset=UTF-8"
        }
      })
        .then(response => {
          this.templates = 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
          });
        });
    },
    deleteTemplate(template) {
      this.$store.commit("application/popup", {
        icon: "trash",
        title: this.$gettext("Delete Template"),
        content:
          this.$gettext(
            "Do you really want to delete the following template:"
          ) +
          `<br>
        <b>${template.name}</b>`,
        confirm: {
          label: this.$gettext("Delete"),
          icon: "trash",
          callback: () => {
            HTTP.delete(
              `/templates/${template.type}/${encodeURIComponent(
                template.name
              )}`,
              {
                headers: {
                  "X-Gemma-Auth": localStorage.getItem("token"),
                  "Content-type": "text/xml; charset=UTF-8"
                }
              }
            ).then(() => {
              let removeIndex = this.templates.findIndex(
                t => t.name === template.name
              );
              if (removeIndex !== -1) {
                this.templates.splice(removeIndex, 1);
                displayInfo({
                  message:
                    template.name + " " + this.$gettext("deleted successfully")
                });
              }
            });
          }
        },
        cancel: {
          label: this.$gettext("Cancel"),
          icon: "times"
        }
      });
    }
  },
  mounted() {
    this.loadTemplates();
  }
};
</script>