<template> <div v-frag> <div class="row"> <div class="fourteen wide column"> <h1 class="ui header"> <div class="content"> {{ feature.title || feature.feature_id }} <div class="ui icon right floated compact buttons"> <!-- {% if permissions|lookup:'can_create_feature' %} --> <router-link :to="{ name: 'ajouter-signalement', params: { slug_type_signal: $route.params.slug_type_signal }, }" class="ui button button-hover-orange" data-tooltip="Ajouter un signalement" data-position="bottom left" > <i class="plus fitted icon"></i> </router-link> <!-- {% endif %} {% if permissions|lookup:'can_update_feature' %} --> <router-link :to="{ name: 'editer-signalement', params: { slug_signal: $route.params.slug_signal, slug_type_signal: $route.params.slug_type_signal }, }" class="ui button button-hover-orange" > <i class="inverted grey pencil alternate icon"></i> </router-link> <!-- {% endif %} {% if permissions|lookup:'can_delete_feature' %} --> <a @click="isCanceling = true" id="feature-delete" class="ui button button-hover-red" > <i class="inverted grey trash alternate icon"></i> </a> <!-- {% endif %} --> </div> <div class="ui hidden divider"></div> <div class="sub header"> {{ feature.description }} <!-- | linebreaks --> </div> </div> </h1> </div> </div> <div class="row"> <div class="seven wide column"> <table class="ui very basic table"> <tbody> <div v-frag v-for="(field, index) in feature.feature_data" :key="'field' + index" > <tr v-if="field"> <td> <b>{{ field.label }}</b> </td> <td> <b> <i v-if=" field.field_type === 'boolean' && field.value === true " class="olive check icon" ></i> <i v-else-if=" field.field_type === 'boolean' && field.value === false " class="red times icon" ></i> <span v-else> {{ field.value }} </span> </b> </td> </tr> </div> <tr> <td>Auteur</td> <td>{{ feature.display_creator }}</td> </tr> <tr> <td>Statut</td> <td> <i v-if="feature.status === 'archived'" class="grey archive icon" ></i> <i v-else-if="feature.status === 'pending'" class="teal hourglass outline icon" ></i> <i v-else-if="feature.status === 'published'" class="olive check icon" ></i> <i v-else-if="feature.status === 'draft'" class="orange pencil alternate icon" ></i> {{ feature.get_status_display }} </td> </tr> <tr> <td>Date de création</td> <td v-if="feature.created_on"> {{ feature.created_on }} </td> </tr> <tr> <td>Date de dernière modification</td> <td v-if="feature.updated_on"> {{ feature.updated_on }} </td> </tr> <tr> <td>Date d'archivage automatique</td> <td v-if="feature.archived_on"> {{ feature.archived_on }} </td> </tr> <tr> <td>Date de suppression automatique</td> <td v-if="feature.deletion_on"> {{ feature.deletion_on }} </td> </tr> </tbody> </table> <!-- <small>{% for link in linked_features %} {% endfor %}</small> // ? EMPTY ?!??? --> <h3>Liaison entre signalements</h3> <table class="ui very basic table"> <tbody> <tr v-for="(link, index) in linked_features" :key="link.feature_to.title + index" > <td> {{ link.relation_type }} <router-link :to="{ name: 'details-signalement', params: { slug_type_signal: link.feature_to.feature_type.slug, slug_signal: link.feature_to.title, }, }" >{{ link.feature_to.title }}</router-link > ({{ link.feature_to.creator }} - {{ link.feature_to.created_on }}) </td> </tr> </tbody> </table> </div> <div class="seven wide column"> <div id="map"></div> </div> </div> <div class="row"> <div class="seven wide column"> <h2 class="ui header">Pièces jointes</h2> <div v-for="pj in attachments" :key="pj.title" class="ui divided items"> <div class="item"> <a class="ui tiny image" target="_blank" :href="pj.attachment_file.url" > <img v-if="pj.extension === '.pdf'" src="{% static 'geocontrib/img/pdf.png' %}" /> <!-- // ? que faire ? --> <img v-else :src="pj.attachment_file.url" /> </a> <div class="middle aligned content"> <a class="header" target="_blank" :href="pj.attachment_file.url" >{{ pj.title }}</a > <div class="description"> {{ pj.info }} </div> </div> </div> </div> <i v-if="attachments.length === 0" >Aucune pièce jointe associée au signalement.</i > </div> <div class="seven wide column"> <h2 class="ui header">Activité et commentaires</h2> <div id="feed-event" class="ui feed"> <div v-frag v-for="(event, index) in events" :key="'event' + index"> <div v-frag v-if="event.event_type === 'create'"> <div v-if="event.object_type === 'feature'" class="event"> <div class="content"> <div class="summary"> <div class="date"> {{ event.created_on }} </div> Création du signalement <span v-if="user">par {{ event.display_user }}</span> </div> </div> </div> <div v-else-if="event.object_type === 'comment'" class="event"> <div class="content"> <div class="summary"> <div class="date"> {{ event.created_on }} </div> Commentaire <span v-if="user">par {{ event.display_user }}</span> </div> <div class="extra text"> {{ event.related_comment.comment }} <div v-frag v-if="event.related_comment.attachments"> <div v-frag v-for="att in event.related_comment.attachments" :key="att.title" > <br /><a :href="att.url" tarrget="_blank" ><i class="paperclip fitted icon"></i> {{ att.title }}</a > </div> </div> </div> </div> </div> </div> <div v-else-if="event.event_type === 'update'" class="event"> <div class="content"> <div class="summary"> <div class="date"> {{ event.created_on }} </div> Signalement mis à jour <span v-if="user">par {{ event.display_user }}</span> </div> </div> </div> </div> </div> <!-- {% if permissions|lookup:'can_create_feature' %} --> <div class="ui segment"> <form id="form-comment" class="ui form" method="POST" enctype="multipart/form-data" > <!-- action="{% url 'geocontrib:add_comment' slug=feature.project.slug feature_type_slug=feature.feature_type.slug feature_id=feature.feature_id%}" --> <!-- {% for hidden in comment_form.hidden_fields %} {{ hidden }} {% endfor %} --> <div v-if="comment_form.non_field_errors" class="alert alert-danger" role="alert" > <span v-for="error in comment_form.non_field_errors" :key="error"> {{ error }} </span> </div> <div class="required field"> <label :for="comment_form.comment.id_for_label" >Ajouter un commentaire</label > {{ comment_form.comment.errors }} <textarea v-model="comment_form.comment.value" :name="comment_form.comment.html_name" rows="2" ></textarea> </div> <label>Pièce jointe (facultative)</label> <div class="two fields"> <div class="field"> <label class="ui icon button" for="attachment_file"> <i class="paperclip icon"></i> <span class="label">{{ comment_form.attachment_file.value ? comment_form.attachment_file.value : "Sélectionner un fichier ..." }}</span> </label> <!-- // todo : get image from "C:\\fakepath\..." --> <input type="file" accept="application/pdf, image/jpeg, image/png" style="display: none" name="attachment_file" id="attachment_file" @change="getAttachmentFileData($event)" /> {{ comment_form.attachment_file.errors }} </div> <div class="field"> <input v-model="comment_form.title.value" type="text" :name="comment_form.title.html_name" :id="comment_form.title.id_for_label" /> {{ comment_form.title.errors }} </div> </div> <button @click="postComment" type="button" class="ui compact green icon button" > <i class="plus icon"></i> Poster le commentaire </button> </form> </div> </div> </div> <div v-if="isCanceling" class="ui dimmer modals page transition visible active" style="display: flex !important" > <div :class="[ 'ui mini modal subscription', { 'active visible': isCanceling }, ]" > <i @click="isCanceling = false" class="close icon"></i> <div class="ui icon header"> <i class="trash alternate icon"></i> Supprimer le signalement </div> <div class="actions"> <form action="{% url 'geocontrib:feature_delete' slug=feature.project.slug feature_type_slug=feature.feature_type.slug feature_id=feature.feature_id %}" method="POST" > <input type="hidden" name="_method" value="delete" /> <button @click="deleteFeature" type="button" class="ui red compact fluid button" > Confirmer la suppression </button> </form> </div> </div> </div> </div> </template> <script> import frag from "vue-frag"; import { mapState } from "vuex"; import { mapUtil } from "@/assets/js/map-util.js"; const axios = require("axios"); export default { name: "Feature_detail", directives: { frag, }, data() { return { isCanceling: false, mock_linked_features: [ /* { relation_type: "Doublon", feature_to: { title: "Éolienne offshore", creator: "Mr Dupont", created_on: new Date().toDateString(), feature_type: { title: "Éolienne", }, }, }, */ ], attachments: [ // TODO : Récupérer depuis l'api /* { attachment_file: { url: "http://localhost:8000/media/user_1/albinoscom.jpg", }, extension: "jpg", title: "albinos", info: "Drôle de bête", }, */ ], // TODO : Récupérer depuis l'api events: [], comment_form: { attachment_file: { errors: null, value: null, }, title: { id_for_label: "title", html_name: "title", errors: null, value: null, }, comment: { id_for_label: "add-comment", html_name: "add-comment", errors: null, value: null, }, non_field_errors: [], }, }; }, computed: { ...mapState(["user"]), feature: function () { return ( this.$store.state.feature.features.find( (el) => el.feature_id === this.$route.params.slug_signal ) || [] ); }, linked_features: function () { // todo: vérifier avec données réels si ça fonctionne correctement //return this.mock_linked_features.filter((el) => el.feature_to); return []; }, }, methods: { postComment() { /* const data = { comment: this.comment_form.comment.value, title: this.comment_form.title.value, attachment_file: this.comment_form.attachment_file.value, }; */ this.$store.dispatch("feature/POST_COMMENT"); //console.log("POST comment", data); }, getAttachmentFileData(evt) { const files = evt.target.files || evt.dataTransfer.files; const period = files[0].name.lastIndexOf("."); const fileName = files[0].name.substring(0, period) const fileExtension = files[0].name.substring(period + 1); const shortName = fileName.slice(0, 10) + "[...]." + fileExtension; this.comment_form.attachment_file.value = shortName; this.comment_form.title.value = shortName; }, deleteFeature() { this.$store.dispatch( "feature/DELETE_FEATURE", this.$route.params.slug_signal ); }, initMap(){ var mapDefaultViewCenter = this.$store.state.configuration.DEFAULT_MAP_VIEW.center; var mapDefaultViewZoom = this.$store.state.configuration.DEFAULT_MAP_VIEW.zoom; this.map=mapUtil.createMap({ mapDefaultViewCenter, mapDefaultViewZoom, }); // Update link to feature list with map zoom and center mapUtil.addMapEventListener("moveend", function() { // update link to feature list with map zoom and center /*var $featureListLink = $("#feature-list-link") var baseUrl = $featureListLink.attr("href").split("?")[0] $featureListLink.attr("href", baseUrl +`?zoom=${this.map.getZoom()}&lat=${this.map.getCenter().lat}&lng=${this.map.getCenter().lng}`)*/ }); // Load the layers. // - if one basemap exists, we load the layers of the first one // - if not, load the default map and service options let layersToLoad = null; var baseMaps; var project=""; var layers=[]; if (baseMaps && baseMaps.length > 0) { // Use active one if exists, otherwise index 0 (first basemap in the list) const mapOptions = JSON.parse(localStorage.getItem('geocontrib-map-options')) || {}; const basemapIndex = mapOptions && mapOptions[project] && mapOptions[project]['current-basemap-index'] ? mapOptions[project]['current-basemap-index'] : 0; layersToLoad = baseMaps[basemapIndex].layers; layersToLoad.forEach(layerToLoad => { layers.forEach(layer => { if (layer.id === layerToLoad.id) { layerToLoad = Object.assign(layerToLoad, layer); } }); }); layersToLoad.reverse() } mapUtil.addLayers(layersToLoad, this.$store.state.configuration.DEFAULT_BASE_MAP.SERVICE, this.$store.state.configuration.DEFAULT_BASE_MAP.OPTIONS); mapUtil.getMap().dragging.disable() mapUtil.getMap().doubleClickZoom.disable() mapUtil.getMap().scrollWheelZoom.disable() var currentFeatureId=this.$route.params.slug_signal; const url=`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`; axios.get(url) .then((response) => { const features = response.data.features; if (features) { const currentFeature = features.filter(feat => feat.id === currentFeatureId); const featureGroup = mapUtil.addFeatures(currentFeature); mapUtil.getMap().fitBounds(featureGroup.getBounds()); } }) .catch((error) => { throw error; }); } }, created() { if (!this.project) { this.$store.dispatch("GET_PROJECT_INFO", this.$route.params.slug); } this.$store.commit( "feature_type/SET_CURRENT_FEATURE_TYPE_SLUG", this.$route.params.slug_type_signal ); }, mounted() { this.initMap(); }, }; </script> <style> #map { width: 100%; height: 100%; min-height: 250px; } #feed-event .event { margin-bottom: 1em; } #feed-event .event .date { margin-right: 1em !important; } #feed-event .event .extra.text { margin-left: 107px; margin-top: 0; } </style>