Skip to content
Snippets Groups Projects
Feature_edit.vue 19.9 KiB
Newer Older
<template>
  <div v-frag>
    <div class="fourteen wide column">
      <h1
        v-if="feature && $router.history.current.name === 'editer-signalement'"
      >
Timothee P's avatar
Timothee P committed
        Mise à jour du signalement "{{ feature.title || feature.feature_id }}"
      <h1
        v-else-if="
          feature_type && $router.history.current.name === 'ajouter-signalement'
        "
      >
        Création d'un signalement <small>[{{ feature_type.title }}]</small>
      </h1>

      <form
        id="form-feature-edit"
        action=""
        method="post"
        enctype="multipart/form-data"
        class="ui form"
      >
        <!-- Feature Fields -->
        <div class="two fields">
          <div class="required field">
            <label :for="form.title.id_for_label">{{ form.title.label }}</label>
              :maxlength="form.title.field.max_length"
              :name="form.title.html_name"
              :id="form.title.id_for_label"
              v-model="form.title.value"
            {{ form.title.errors }}
            <label :for="form.status.id_for_label">{{
              form.status.label
              :options="form.status.field.choices"
              :selected="selected_status"
              :selection.sync="selected_status"
            {{ form.status.errors }}
          <label :for="form.description.id_for_label">{{
            form.description.label
            :name="form.description.html_name"
            v-model="form.description.value"
          {{ form.description.errors }}
          <label :for="form.geom.id_for_label">{{ form.geom.label }}</label>
          <div v-frag v-if="feature_type && feature_type.geom_type === 'point'">
            <p>
              <button
                id="add-geo-image"
                type="button"
                class="ui compact button"
              >
                <i class="file image icon"></i>Importer une image géoréférencée
              </button>
              Vous pouvez utiliser une image géoréférencée pour localiser le
              signalement.
            </p>
            <p>
              <button
                id="create-point-geoposition"
                type="button"
                class="ui compact button"
              >
                <i class="ui map marker alternate icon"></i>Positionner le
                signalement à partir de votre géolocalisation
              </button>
            </p>
            <span id="erreur-geolocalisation" style="display: none">
              <div class="ui negative message">
                <div class="header">
                  Une erreur est survenue avec la fonctionnalité de
                  géolocalisation
                </div>
                <p id="erreur-geolocalisation-message"></p>
          {{ form.geom.errors }}
            :name="form.geom.html_name"
            :id="form.geom.id_for_label"
            v-model="form.geom.value"
          />
          <div class="ui tab active map-container" data-tab="map">
            <div id="map"></div>
            <!-- // todo: ajouter v-if -->
            <!-- {% if serialized_base_maps|length > 0 %} {% include
            "geocontrib/map-layers/sidebar-layers.html" with
            basemaps=serialized_base_maps layers=serialized_layers
            project=project.slug%} {% endif %} -->
          </div>
        </div>

        <!-- Extra Fields -->
        <div class="ui horizontal divider">DONNÉES MÉTIER</div>
        <!-- // Todo: Récupérer les "extra_form" de l'API -->
        <div
          v-for="(field, index) in extra_form_with_values"
          :key="field.field_type + index"
          class="field"
        >
          <div v-frag v-if="field.field_type === 'char'">
            <label for="field.name">{{ field.label }}</label>
              type="text"
              :name="field.name"
              :id="field.name"
              v-model="field.value"
              @blur="updateStore_extra_form"
          </div>

          <div v-frag v-else-if="field.field_type === 'list'">
            <label for="field.name">{{ field.label }}</label>
            <Dropdown
              :options="field.choices"
              :selected="selected_extra_form_list"
              :selection.sync="selected_extra_form_list"
            />
          </div>
          <div v-frag v-else-if="field.field_type === 'integer'">
            <label for="field.name">{{ field.label }}</label>
            <div class="ui input">
              <!-- //* si click sur fléche dans champ input, pas de focus, donc pas de blur, donc utilisation de @change -->
              <input
                type="number"
                :name="field.name"
                :id="field.name"
                v-model.number="field.value"
                @change="updateStore_extra_form"
              />
            </div>
          </div>
          <div v-frag v-else-if="field.field_type === 'boolean'">
            <div class="ui checkbox">
              <input
                type="checkbox"
                :checked="field.value"
                :name="field.name"
                :id="field.name"
                @change="updateStore_extra_form"
              />
              <label for="field.name">{{ field.label }}</label>
          <div v-frag v-else-if="field.field_type === 'date'">
            <label for="field.name">{{ field.label }}</label>
              type="date"
              :name="field.name"
              :id="field.name"
              v-model="field.value"
              @blur="updateStore_extra_form"
          <div v-frag v-else-if="field.field_type === 'decimal'">
            <label for="field.name">{{ field.label }}</label>
            <div class="ui input">
              <input
                type="number"
                step=".01"
                :name="field.name"
                :id="field.name"
                v-model.number="field.value"
                @change="updateStore_extra_form"
              />
            </div>
          <div v-frag v-else-if="field.field_type === 'text'">
            <label :for="field.name">{{ field.label }}</label>
            <textarea
              rows="3"
              @blur="updateStore_extra_form"
            ></textarea>
          </div>
          {{ field.errors }}
        </div>

        <!-- Pièces jointes -->
        <div class="ui horizontal divider">PIÈCES JOINTES</div>
        <!-- {{ attachment_formset.non_form_errors }} -->
          <!-- {{ attachment_formset.management_form }} -->
          <FeatureAttachmentForm
            v-for="form in attachmentFormset"
            :key="form.dataKey"
            :attachmentForm="form"
          />
          @click="add_attachement_formset"
          id="add-attachment"
          type="button"
          class="ui compact basic button button-hover-green"
        >
          <i class="ui plus icon"></i>Ajouter une pièce jointe
        </button>

        <!-- Signalements liés -->
        <div class="ui horizontal divider">SIGNALEMENTS LIÉS</div>
        <!-- {{ linked_formset.non_form_errors }} -->
          <!-- {{ linked_formset.management_form }} -->

          <FeatureLinkedForm
            v-for="form in linkedFormset"
            :key="form.dataKey"
            :linkedForm="form"
            :features="features"
          />
          @click="add_linked_formset"
          id="add-link"
          type="button"
          class="ui compact basic button button-hover-green"
        >
          <i class="ui plus icon"></i>Ajouter une liaison
        </button>

        <div class="ui divider"></div>

        <button @click="postForm" type="button" class="ui teal icon button">
          <i class="white save icon"></i> Enregistrer les changements
        </button>
      </form>
    </div>
  </div>
</template>

<script>
import frag from "vue-frag";
import { mapGetters, mapState } from "vuex";
import FeatureAttachmentForm from "@/components/feature/FeatureAttachmentForm";
import FeatureLinkedForm from "@/components/feature/FeatureLinkedForm";
import Dropdown from "@/components/Dropdown.vue";
import SidebarLayers from "@/components/map-layers/SidebarLayers";
DESPRES Damien's avatar
DESPRES Damien committed
import L from "leaflet";
import "leaflet-draw";
import { mapUtil } from "@/assets/js/map-util.js";
const axios = require("axios");

export default {
  name: "Feature_edit",
  directives: {
    frag,
  },

  components: {
    FeatureAttachmentForm,
    FeatureLinkedForm,
  },

  computed: {
    ...mapState(["project"]),
    ...mapState("feature", [
      "attachmentFormset",
      "linkedFormset",
      "features",
      "extra_form",
    ]),
    ...mapGetters("feature_type", ["feature_type"]),

Timothee P's avatar
Timothee P committed
    feature: function () {
      return this.$store.state.feature.features.find(
        (el) => el.feature_id === this.$route.params.slug_signal
        return this.form.status.value;
        this.form.status.value = newValue;
Timothee P's avatar
Timothee P committed
    extra_form_with_values: function () {
      return this.extra_form.map((el) => {
        return { ...el, value: el.value ? el.value : null };
      });
    },
    selected_extra_form_list: {
      get() {
        return this.extra_form_with_values.find(
          (el) => el.field_type === "list"
        ).value;
      },
      set(newValue) {
        const index = this.extra_form_with_values.findIndex(
          (el) => el.field_type === "list"
        );
        this.extra_form_with_values[index].value = newValue;
        this.updateStore_extra_form();
      },
    },
      attachmentDataKey: 0,
      linkedDataKey: 0,
      form: {
          errors: null,
          id_for_label: "name",
          html_name: "name",
          label: "Nom",
          value: "",
          errors: null,
          id_for_label: "status",
            choices: ["Brouillon", "Publié", "Archivé"],
          html_name: "status",
          label: "Statut",
          value: "Brouillon",
          errors: null,
          id_for_label: "description",
          html_name: "description",
          label: "Description",
          value: "",
          label: "Localisation",
    add_attachement_formset() {
      this.$store.commit("feature/ADD_ATTACHMENT_FORM", this.attachmentDataKey); // * create an object with the counter in store
      this.attachmentDataKey += 1; // * increment counter for key in v-for
    },
    add_linked_formset() {
      this.$store.commit("feature/ADD_LINKED_FORM", this.linkedDataKey); // * create an object with the counter in store
      this.linkedDataKey += 1; // * increment counter for key in v-for
    updateStore() {
      this.$store.commit("feature/UPDATE_FORM", {
        title: this.form.title.value,
        status: this.form.status.value,
        description: this.form.description,
    updateStore_extra_form() {
      this.$store.commit(
        "feature/UPDATE_EXTRA_FORM",
        this.extra_form_with_values
      );
    },

    postForm() {
      if (this.form.title.value) {
        this.form.title.errors = null;
        this.$store.dispatch("feature/POST_FEATURE");
      } else {
        this.form.title.errors = "Veuillez compléter ce champ.";
      }
    },
DESPRES Damien's avatar
DESPRES Damien committed

    initMap(){
      var geomLeaflet = {
        'point': 'circlemarker',
        'linestring': 'polyline',
        'polygon': 'polygon'
      }
      var geomType = "{{ feature_type.geom_type }}"
      var drawConfig = {
        polygon: false,
        marker: false,
        polyline: false,
        rectangle: false,
        circle: false,
        circlemarker: false,
      }
      drawConfig[geomLeaflet[geomType]] = true

      L.drawLocal = {
        draw: {
          toolbar: {
            actions: {
              title: 'Annuler le dessin',
              text: 'Annuler'
            },
            finish: {
              title: 'Terminer le dessin',
              text: 'Terminer'
            },
            undo: {
              title: 'Supprimer le dernier point dessiné',
              text: 'Supprimer le dernier point'
            },
            buttons: {
              polyline: 'Dessiner une polyligne',
              polygon: 'Dessiner un polygone',
              rectangle: 'Dessiner un rectangle',
              circle: 'Dessiner un cercle',
              marker: 'Dessiner une balise',
              circlemarker: 'Dessiner un point'
            }
          },
          handlers: {
            circle: {
              tooltip: {
                start: 'Cliquer et glisser pour dessiner le cercle.'
              },
              radius: 'Rayon'
            },
            circlemarker: {
              tooltip: {
                start: 'Cliquer sur la carte pour placer le point.'
              }
            },
            marker: {
              tooltip: {
                start: 'Cliquer sur la carte pour placer la balise.'
              }
            },
            polygon: {
              tooltip: {
                start: 'Cliquer pour commencer à dessiner.',
                cont: 'Cliquer pour continuer à dessiner.',
                end: 'Cliquer sur le premier point pour terminer le dessin.'
              }
            },
            polyline: {
              error: '<strong>Error:</strong> shape edges cannot cross!',
              tooltip: {
                start: 'Cliquer pour commencer à dessiner.',
                cont: 'Cliquer pour continuer à dessiner.',
                end: 'Cliquer sur le dernier point pour terminer le dessin.'
              }
            },
            rectangle: {
              tooltip: {
                start: 'Cliquer et glisser pour dessiner le rectangle.'
              }
            },
            simpleshape: {
              tooltip: {
                end: 'Relâcher la souris pour terminer de dessiner.'
              }
            }
          }
        },
        edit: {
          toolbar: {
            actions: {
              save: {
                title: 'Sauver les modifications',
                text: 'Sauver'
              },
              cancel: {
                title: 'Annuler la modification, annule toutes les modifications',
                text: 'Annuler'
              },
              clearAll: {
                title: 'Effacer l\'objet',
                text: 'Effacer'
              }
            },
            buttons: {
              edit: 'Modifier l\'objet',
              editDisabled: 'Aucun objet à modifier',
              remove: 'Supprimer l\'objet',
              removeDisabled: 'Aucun objet à supprimer'
            }
          },
          handlers: {
            edit: {
              tooltip: {
                text: 'Faites glisser les marqueurs ou les balises pour modifier l\'élément.',
                subtext: 'Cliquez sur Annuler pour annuler les modifications..'
              }
            },
            remove: {
              tooltip: {
                text: 'Cliquez sur un élément pour le supprimer.'
              }
            }
          }
        }
      };


      var drawnItems = new L.FeatureGroup();
DESPRES Damien's avatar
DESPRES Damien committed
      //console.log(drawnItems);
      //console.log(configuration);
DESPRES Damien's avatar
DESPRES Damien committed
      var mapDefaultViewCenter = this.$store.state.configuration.DEFAULT_MAP_VIEW.center;
      var mapDefaultViewZoom = this.$store.state.configuration.DEFAULT_MAP_VIEW.zoom;
DESPRES Damien's avatar
DESPRES Damien committed

      // Create the map, then init the layers and features
      this.map = mapUtil.createMap({
          mapDefaultViewCenter,
          mapDefaultViewZoom
        });

      // mapUtil.addLayers(layers, serviceMap, optionsMap);
      const currentFeatureId="12";
DESPRES Damien's avatar
DESPRES Damien committed
      const url=this.$store.state.configuration.BASE_URL+"/test_data/features.json";
DESPRES Damien's avatar
DESPRES Damien committed
      axios.get(url)
        .then((response) => {
          const features = response.data.features;
          if (features) {
            const allFeaturesExceptCurrent = features.filter(feat => feat.id !== currentFeatureId);
            mapUtil.addFeatures(allFeaturesExceptCurrent);
          }
        })
        .catch((error) => {
          throw error;
        });
      
      let self=this;
    // ------   Listen Sidebar events ---------- //
    // Listen custom events triggered by the sidebar-layers
    document.addEventListener('add-layers', (event) => {
      mapUtil.removeLayers(self.map);
      // Reverse is done because the first layer in order has to be added in the map in last.
      // Slice is done because reverse() changes the original array, so we make a copy first
DESPRES Damien's avatar
DESPRES Damien committed
      mapUtil.addLayers(event.detail.slice().reverse(), this.$store.state.configuration.DEFAULT_BASE_MAP.SERVICE, this.$store.state.configuration.DEFAULT_BASE_MAP.OPTIONS);
DESPRES Damien's avatar
DESPRES Damien committed
    });

    document.addEventListener('update-opacity', (event) => {
      mapUtil.updateOpacity(event.detail.layerId, event.detail.opacity);
    });

    document.addEventListener('change-layers-order', (event) => {
      // Reverse is done because the first layer in order has to be added in the map in last.
      // Slice is done because reverse() changes the original array, so we make a copy first
      mapUtil.updateOrder(event.detail.layers.slice().reverse());
    });

    // --------- End sidebar events ----------
    // Check if at least one basemap exist. If not, use the default application
      const basemaps = undefined;//JSON.parse(document.getElementById('basemaps').textContent);
      if (!basemaps || basemaps && basemaps.length === 0) {
DESPRES Damien's avatar
DESPRES Damien committed
        mapUtil.addLayers(null, this.$store.state.configuration.DEFAULT_BASE_MAP.SERVICE, this.$store.state.configuration.DEFAULT_BASE_MAP.OPTIONS);
DESPRES Damien's avatar
DESPRES Damien committed
      }
  this.map.addLayer(drawnItems);

  var drawControlFull = new L.Control.Draw({
    position: 'topright',
    edit: {
      featureGroup: drawnItems
    },
    draw: drawConfig,
  })

  /*var drawControlEditOnly = new L.Control.Draw({
    position: 'topright',
    edit: {
      featureGroup: drawnItems
    },
    draw: false
  })*/
  this.map.addControl(drawControlFull);


    }
  created() {
    if (!this.project) {
      this.$store.dispatch("GET_PROJECT_INFO", this.$route.params.slug);
Timothee P's avatar
Timothee P committed
    this.$store.commit(
      "feature_type/SET_CURRENT_FEATURE_TYPE_SLUG",
      this.$route.params.slug_type_signal
    );
Timothee P's avatar
Timothee P committed
    if (this.$router.history.current.name === "editer-signalement") {
      for (let el in this.feature) {
        if (el && this.form[el]) this.form[el].value = this.feature[el];
      }
    }
DESPRES Damien's avatar
DESPRES Damien committed
    this.initMap();
   
};

// TODO : add script from django and convert:
</script>

<style>
#map {
  height: 70vh;
  width: 100%;

@media only screen and (max-width: 767px) {
  #map {
    height: 80vh;
  }
}
/* // ! missing style in semantic.min.css, je ne comprends pas comment... */
.ui.right.floated.button {
  float: right;
  margin-right: 0;
  margin-left: 0.25em;
}
/* // ! margin écrasé par class last-child first-child, pas normal ... */
.ui.segment {
  margin: 1rem 0 !important;
}