view client/src/components/systemconfiguration/MorphologyClassbreaks.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 2e19ed576c68
children 84d01a536bec
line wrap: on
line source

<template>
  <div class="d-flex flex-column py-4">
    <div class="px-3">
      <h6 class="font-weight-bold"><translate>Sounding Result</translate></h6>
      <div class="d-flex flex-wrap">
        <div
          class="input-group mb-3 mr-2 classbreak"
          v-for="(value, i) in morphologyClassbreaks"
          :key="i"
        >
          <div class="input-group-prepend">
            <button
              :class="[
                'btn btn-sm btn-outline-secondary',
                { hasColor: morphologyClassbreaks[i][1] !== '#ffffff' }
              ]"
              :style="
                'width: 28px; background-color: ' +
                  (morphologyClassbreaks[i][1] || 'transparent')
              "
              type="button"
              @click="showColorPicker('sounding-' + i)"
            >
              <font-awesome-icon icon="tint" />
            </button>
            <div
              class="color-picker card shadow-sm"
              v-if="activeColorPicker === 'sounding-' + i"
            >
              <UIBoxHeader
                :title="colorPickerTitle"
                icon="paint-brush"
                :actions="[
                  {
                    callback: () => {
                      morphologyClassbreaks[i][1] = '#ffffff';
                      activeColorPicker = null;
                    },
                    icon: 'trash'
                  },
                  {
                    callback: () => {
                      activeColorPicker = null;
                    },
                    icon: 'check'
                  }
                ]"
              />
              <chrome-picker
                v-model="morphologyClassbreaks[i][1]"
                @input="color => (morphologyClassbreaks[i][1] = color.hex)"
              />
            </div>
          </div>
          <form id="novalidatedform" />
          <input
            v-model.number="morphologyClassbreaks[i][0]"
            type="number"
            min="0"
            step="0.01"
            class="form-control form-control-sm numfield"
          />
          <div class="input-group-append">
            <button
              class="btn btn-sm btn-outline-secondary"
              type="button"
              @click="morphologyClassbreaks.splice(i, 1)"
            >
              <font-awesome-icon icon="times" />
            </button>
            <button
              class="btn btn-sm btn-outline-secondary"
              @click="addClassbreak(morphologyClassbreaks, i)"
            >
              <font-awesome-icon icon="plus" />
            </button>
          </div>
        </div>
        <button
          v-if="!morphologyClassbreaks.length"
          class="btn btn-sm btn-success mb-3"
          @click="addClassbreak(morphologyClassbreaks, 0)"
        >
          <font-awesome-icon icon="plus" />
        </button>
      </div>
    </div>
    <div class="mb-4 px-3">
      <a
        @click.prevent="submitClassbreaks"
        :class="[
          'btn btn-info btn-sm text-white',
          { disabled: !checkClassbreaks }
        ]"
      >
        <translate>Send</translate>
      </a>
      <a
        @click.prevent="resetClassbreaks"
        class="btn btn-outline-info btn-sm ml-2"
      >
        <translate>Reset to defaults</translate>
      </a>
      <span class="text-danger" v-if="!checkClassbreaks">
        {{ validationMessage }}
      </span>
      <span class="text-secondary fix-trans-space" v-translate>
        Changes need a map reload. Consider informing your users.
      </span>
    </div>
    <div class="px-3">
      <h6 class="font-weight-bold">
        <translate>Sounding Result Comparison</translate>
      </h6>
      <div class="d-flex flex-wrap">
        <div
          class="input-group mb-3 mr-2 classbreak"
          v-for="(value, i) in morphologyClassbreaksCompare"
          :key="i"
        >
          <div class="input-group-prepend">
            <button
              :class="[
                'btn btn-sm btn-outline-secondary',
                { hasColor: morphologyClassbreaksCompare[i][1] !== '#ffffff' }
              ]"
              :style="
                'width: 28px; background-color: ' +
                  (morphologyClassbreaksCompare[i][1] || 'transparent')
              "
              type="button"
              @click="showColorPicker('compare-' + i)"
            >
              <font-awesome-icon icon="tint" />
            </button>
            <div
              class="color-picker card shadow-sm"
              v-if="activeColorPicker === 'compare-' + i"
            >
              <UIBoxHeader
                :title="colorPickerTitle"
                icon="paint-brush"
                :actions="[
                  {
                    callback: () => {
                      morphologyClassbreaksCompare[i][1] = '#ffffff';
                      activeColorPicker = null;
                    },
                    icon: 'trash'
                  },
                  {
                    callback: () => {
                      activeColorPicker = null;
                    },
                    icon: 'check'
                  }
                ]"
              />
              <chrome-picker
                v-model="morphologyClassbreaksCompare[i][1]"
                @input="
                  color => (morphologyClassbreaksCompare[i][1] = color.hex)
                "
              />
            </div>
          </div>
          <input
            v-model.number="morphologyClassbreaksCompare[i][0]"
            type="number"
            step="0.01"
            class="form-control form-control-sm numfield"
          />
          <div class="input-group-append">
            <button
              class="btn btn-sm btn-outline-secondary"
              type="button"
              @click="morphologyClassbreaksCompare.splice(i, 1)"
            >
              <font-awesome-icon icon="times" />
            </button>
            <button
              class="btn btn-sm btn-outline-secondary"
              @click="addClassbreak(morphologyClassbreaksCompare, i)"
            >
              <font-awesome-icon icon="plus" />
            </button>
          </div>
        </div>
        <button
          v-if="!morphologyClassbreaksCompare.length"
          class="btn btn-sm btn-success mb-3"
          @click="addClassbreak(morphologyClassbreaksCompare, 0)"
        >
          <font-awesome-icon icon="plus" />
        </button>
      </div>
    </div>
    <div class="px-3">
      <a
        @click.prevent="submitClassbreaksCompare"
        :class="[
          'btn btn-info btn-sm text-white',
          { disabled: !checkClassbreaksCompare }
        ]"
      >
        <translate>Send</translate>
      </a>
      <a
        @click.prevent="resetClassbreaksCompare"
        class="btn btn-outline-info btn-sm ml-2"
      >
        <translate>Reset to defaults</translate>
      </a>
      <span class="text-danger" v-if="!checkClassbreaksCompare">
        {{ validationMessageForCompare }}
      </span>
      <span class="text-secondary fix-trans-space" v-translate>
        Colour changes need a map reload. Value changes need a de- and re-select
        of a difference calculation. Inform your users!
      </span>
    </div>
  </div>
</template>

<style lang="sass" scoped>
.numfield:invalid
 border: 2px solid
 border-color: #ff0000

.classbreak
  width: 154px
  .btn-outline-secondary
    border-color: #ccc
    color: #ccc
    &:hover:not(.hasColor)
      background: #eee !important
  .input-group-prepend
    .btn-outline-secondary.hasColor
      color: rgba(255, 255, 255, 0.5)
  .input-group-append
    .btn-outline-secondary
      &:hover
        color: #dc3545
      &:last-child:hover
        color: #28a745
  .color-picker
    position: absolute
    top: -4px
    left: 19px
    z-index: 9
    overflow: hidden
    border-top-left-radius: 0 !important
    .btn
      border-radius: 0 !important
    .vc-chrome
      box-shadow: none
/deep/
  .vc-chrome-alpha-wrap
    display: none !important
  .vc-chrome-hue-wrap
    margin-top: 10px
  .vc-chrome-saturation-wrap
    border-radius: 0
</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@intevation.de>
 */
import { mapState } from "vuex";
import { Chrome } from "vue-color";
import defaults from "./defaults";

export default {
  components: {
    "chrome-picker": Chrome
  },
  data() {
    return {
      morphologyClassbreaks: [],
      morphologyClassbreaksCompare: [],
      activeColorPicker: null,
      closeColorPickerListener: null,
      validationMessage: "",
      validationMessageForCompare: ""
    };
  },
  computed: {
    ...mapState("application", ["config"]),
    colorPickerTitle() {
      return this.$gettext("Choose color");
    },
    checkClassbreaks() {
      return (
        this.valuesAreValid(this.morphologyClassbreaks) &&
        !this.hasDoublettes(this.morphologyClassbreaks)
      );
    },
    checkClassbreaksCompare() {
      return (
        this.valuesAreValid(this.morphologyClassbreaksCompare, "compare") &&
        !this.hasDoublettes(this.morphologyClassbreaksCompare, "compare")
      );
    }
  },
  methods: {
    // check if the same value is used for more than one field.
    hasDoublettes(m, compare) {
      const errorMessage = this.$gettext(
        "Same value is used in multiple fields."
      );
      let values = [];
      for (let i = 0; i < m.length; i++) {
        values[i] = Number(m[i][0]);
      }
      if (new Set(values).size !== values.length) {
        //  determine which message to change
        if (compare !== "compare") {
          this.validationMessage = errorMessage;
        } else {
          this.validationMessageForCompare = errorMessage;
        }
        return true;
      }
      return false;
    },
    valuesAreValid(m, compare) {
      const errorMessage = this.$gettext(
        "There are invalid classbreak values."
      );
      let values = m.map(e => {
        const element = e[0];
        if (!isNaN(element)) {
          if (element === "") return false; // check if the field is empty
          if (!isNaN(element)) {
            const numberParts = String(element).split("."); // check number of decimal places
            return numberParts.length == 2 ? numberParts[1].length < 3 : true;
          }
        }
        return false;
      });
      if (values.every(e => e === true)) {
        return true;
      }
      if (compare !== "compare") {
        this.validationMessage = errorMessage;
      } else {
        this.validationMessageForCompare = errorMessage;
      }
      return false;
    },
    addClassbreak(classbreaks, i) {
      classbreaks.splice(
        i,
        0,
        classbreaks.length > i
          ? [classbreaks[i][0], classbreaks[i][1]] // create new array!
          : [1, "#ffffff"]
      );
    },
    showColorPicker(id) {
      this.activeColorPicker = this.activeColorPicker === id ? null : id;
    },
    submitClassbreaks() {
      this.$store
        .dispatch("application/saveConfig", {
          morphology_classbreaks: this.morphologyClassbreaks
            .sort((a, b) => (a[0] < b[0] ? -1 : 1))
            .map(cb => (cb[1] === "#ffffff" ? cb[0] : cb.join(":")))
            .join(",")
        })
        .finally(() => this.$store.dispatch("application/loadConfig"));
    },
    submitClassbreaksCompare() {
      this.$store
        .dispatch("application/saveConfig", {
          morphology_classbreaks_compare: this.morphologyClassbreaksCompare
            .sort((a, b) => (a[0] < b[0] ? -1 : 1))
            .map(cb => (cb[1] === "#ffffff" ? cb[0] : cb.join(":")))
            .join(",")
        })
        .finally(() => this.$store.dispatch("application/loadConfig"));
    },
    resetClassbreaks() {
      this.morphologyClassbreaks = this.parseClassbreakString(
        defaults.morphology_classbreaks
      );
    },
    resetClassbreaksCompare() {
      this.morphologyClassbreaksCompare = this.parseClassbreakString(
        defaults.morphology_classbreaks_compare
      );
    },
    parseClassbreakString(str) {
      return str
        .split(",")
        .map(cb => cb.split(":"))
        .map(cb => {
          cb[0] = Number(cb[0]);
          cb[1] = cb[1] || "#ffffff";
          return cb;
        });
    }
  },
  mounted() {
    this.morphologyClassbreaks = this.parseClassbreakString(
      this.config.morphology_classbreaks
    );
    this.morphologyClassbreaksCompare = this.parseClassbreakString(
      this.config.morphology_classbreaks_compare
    );

    this.closeColorPickerListener = e => {
      // Escape
      if (e.keyCode === 27) {
        this.activeColorPicker = null;
      }
    };
    window.addEventListener("keydown", this.closeColorPickerListener);
  },
  destroyed() {
    window.removeEventListener("keydown", this.closeColorPickerListener);
  }
};
</script>