<template> <div v-frag> <div v-frag v-if="permissions.can_view_project && project"> <div id="message" class="fullwidth"> <div v-if="tempMessage" class="ui positive message"> <!-- <i class="close icon"></i> --> <!-- <div class="header">You are eligible for a reward</div> --> <p><i class="check icon"></i> {{ tempMessage }}</p> </div> </div> <div class="row"> <div class="four wide middle aligned column"> <img class="ui small spaced image" :src=" project.thumbnail.includes('default') ? require('@/assets/img/default.png') : DJANGO_BASE_URL + project.thumbnail + refreshId() " /> <div class="ui hidden divider"></div> <div class="ui basic teal label" data-tooltip="Membres"> <i class="user icon"></i>{{ project.nb_contributors }} </div> <div class="ui basic teal label" data-tooltip="Signalements"> <i class="map marker icon"></i>{{ project.nb_published_features }} </div> <div class="ui basic teal label" data-tooltip="Commentaires"> <i class="comment icon"></i >{{ project.nb_published_features_comments }} </div> </div> <div class="ten wide column"> <h1 class="ui header"> <div class="content"> {{ project.title }} <div class="ui icon right floated compact buttons"> <a v-if="permissions.can_view_project" id="subscribe-button" class="ui button button-hover-green" data-tooltip="S'abonner au projet" data-position="top center" data-variation="mini" @click="isModalOpen = true" > <i class="inverted grey envelope icon"></i> </a> <router-link v-if="permissions.can_update_project" :to="{ name: 'project_edit', params: { slug: project.slug } }" class="ui button button-hover-orange" data-tooltip="Modifier le projet" data-position="top center" data-variation="mini" > <i class="inverted grey pencil alternate icon"></i> </router-link> </div> <div class="ui hidden divider"></div> <div class="sub header"> {{ project.description }} </div> </div> </h1> </div> </div> <div class="row"> <div class="seven wide column"> <div class="ui middle aligned divided list"> <div v-for="(type, index) in feature_types" :key="type.title + '-' + index" class="item" > <div class="middle aligned content"> <router-link :to="{ name: 'details-type-signalement', params: { feature_type_slug: type.slug }, }" > <img v-if="type.geom_type == 'point'" class="list-image-type" src="@/assets/img/marker.png" /> <img v-if="type.geom_type == 'linestring'" class="list-image-type" src="@/assets/img/line.png" /> <img v-if="type.geom_type == 'polygon'" class="list-image-type" src="@/assets/img/polygon.png" /> {{ type.title }} </router-link> <!-- {% if project and feature_types and permissions|lookup:'can_create_feature' %} --> <!-- // ? should we get type.is_editable ? --> <!-- v-if=" project && permissions.can_create_feature && type.is_editable " --> <router-link v-if="project && permissions.can_create_feature" :to="{ name: 'ajouter-signalement', params: { slug_type_signal: type.slug }, }" class=" ui compact small icon right floated button button-hover-green " data-tooltip="Ajouter un signalement" data-position="left center" data-variation="mini" > <i class="ui plus icon"></i> </router-link> <router-link :to="{ name: 'dupliquer-type-signalement', params: { slug_type_signal: type.slug }, }" v-if="project && permissions.can_create_feature" class=" ui compact small icon right floated button button-hover-green " data-tooltip="Dupliquer un type de signalement" data-position="left center" data-variation="mini" > <i class="inverted grey copy alternate icon"></i> </router-link> <router-link :to="{ name: 'editer-type-signalement', params: { slug_type_signal: type.slug }, }" v-if="project && type.is_editable" class=" ui compact small icon right floated button button-hover-green " data-tooltip="Éditer le type de signalement" data-position="left center" data-variation="mini" > <i class="inverted grey pencil alternate icon"></i> </router-link> <!-- {% endif %} --> </div> </div> <div v-if="feature_types.length === 0"> <i> Le projet ne contient pas encore de type de signalements. </i> </div> </div> <div class="nouveau-type-signalement"> <router-link v-if="permissions.can_update_project" :to="{ name: 'ajouter-type-signalement', params: { slug: project.slug }, }" class="ui compact basic button button-hover-green" > <i class="ui plus icon"></i>Créer un nouveau type de signalement </router-link> </div> <div class="nouveau-type-signalement"> <div class="ui compact basic button button-hover-green"> <div> <label class="ui" for="json_file"> <i class="ui plus icon"></i> <span class="label" >Créer un nouveau type de signalement à partir d'un GeoJSON</span > </label> <input type="file" accept="application/json, .json, .geojson" style="display: none" name="json_file" id="json_file" @change="onFileChange" /> </div> </div> <br /> <div id="button-import" v-if="fileToImport.size > 0"> <button :disabled="fileToImport.size == 0" @click="toNewFeatureType" class="ui fluid teal icon button" > <i class="upload icon"></i> Lancer l'import avec le fichier {{ fileToImport.name }} </button> </div> </div> </div> <div class="seven wide column"> <div id="map"></div> </div> </div> <div class="row"> <div class="fourteen wide column"> <div class="ui two stackable cards"> <div class="red card"> <div class="content"> <div class="center aligned header">Derniers signalements</div> <div class="center aligned description"> <div class="ui relaxed list"> <div v-for="(item, index) in last_features" :key="item.title + index" class="item" > <div class="content"> <div> <router-link :to="{ name: 'details-signalement', params: { slug: project.slug, slug_type_signal: item.feature_type.slug, slug_signal: item.feature_id, }, }" >{{ item.title || item.feature_id }}</router-link > </div> <div class="description"> <i >[{{ item.created_on | setDate }}<span v-if="user && item.display_creator" >, par {{ item.display_creator }} </span> ]</i > </div> </div> </div> <i v-if="last_features.length === 0" >Aucun signalement pour le moment.</i > </div> </div> </div> </div> <div class="orange card"> <div class="content"> <div class="center aligned header">Derniers commentaires</div> <div class="center aligned description"> <div class="ui relaxed list"> <div v-for="(item, index) in last_comments" :key="'comment ' + index" class="item" > <div class="content"> <div> <router-link :to="item.related_feature.feature_url" >"{{ item.comment }}"</router-link > </div> <div class="description"> <i >[ {{ item.created_on }}<span v-if="user && item.display_author" >, par {{ item.display_author }} </span> ]</i > </div> </div> </div> <i v-if="!last_comments || last_comments.length === 0" >Aucun commentaire pour le moment.</i > </div> </div> </div> </div> </div> </div> </div> <div class="row"> <div class="fourteen wide column"> <div class="ui grey segment"> <h3 class="ui header">Paramètres du projet</h3> <div class="ui five stackable cards"> <div class="card"> <div class="center aligned content"> <h4 class="ui center aligned icon header"> <i class="disabled grey archive icon"></i> <div class="content">Délai avant archivage automatique</div> </h4> </div> <div class="center aligned extra content"> {{ project.archive_feature }} jours </div> </div> <div class="card"> <div class="content"> <h4 class="ui center aligned icon header"> <i class="disabled grey trash alternate icon"></i> <div class="content"> Délai avant suppression automatique </div> </h4> </div> <div class="center aligned extra content"> {{ project.delete_feature }} jours </div> </div> <div class="card"> <div class="content"> <h4 class="ui center aligned icon header"> <i class="disabled grey eye icon"></i> <div class="content"> Visibilité des signalements publiés </div> </h4> </div> <div class="center aligned extra content"> {{ project.access_level_pub_feature }} </div> </div> <div class="card"> <div class="content"> <h4 class="ui center aligned icon header"> <i class="disabled grey eye icon"></i> <div class="content"> Visibilité des signalements archivés </div> </h4> </div> <div class="center aligned extra content"> {{ project.access_level_arch_feature }} </div> </div> <div class="card"> <div class="content"> <h4 class="ui center aligned icon header"> <i class="disabled grey cogs icon"></i> <div class="content">Modération</div> </h4> </div> <div class="center aligned extra content"> {{ project.moderation ? "Oui" : "Non" }} </div> </div> </div> </div> </div> </div> </div> <span v-else> <i class="icon exclamation triangle"></i> <span >Vous ne disposez pas des droits nécessaires pour consulter ce projet.</span > </span> <div v-if="isModalOpen" class="ui dimmer modals page transition visible active" style="display: flex !important" > <div :class="[ 'ui mini modal subscription', { 'transition visible active': isModalOpen }, ]" > <i @click="isModalOpen = false" class="close icon"></i> <div class="ui icon header"> <i class="envelope icon"></i> Notifications du projet </div> <div class="content"> <button @click="subsribeProject" :class="['ui compact fluid button', is_suscriber ? 'red' : 'green']" > {{ is_suscriber ? "Se désabonner de ce projet" : "S'abonner à ce projet" }} </button> </div> </div> </div> </div> </template> <script> import frag from "vue-frag"; import { mapUtil } from "@/assets/js/map-util.js"; import { mapGetters, mapState } from "vuex"; import projectAPI from "@/services/project-api"; const axios = require("axios"); export default { name: "Project_details", props: ["message"], directives: { frag, }, filters: { setDate: function (value) { let date = new Date(value); let d = date.toLocaleDateString("fr", { year: "2-digit", month: "numeric", day: "numeric", }); return d; }, }, data() { return { geojsonImport: [], fileToImport: { name: "", size: 0 }, slug: this.$route.params.slug, isModalOpen: false, is_suscriber: false, tempMessage: null, }; }, computed: { ...mapGetters(["project", "permissions"]), ...mapState("feature_type", ["feature_types"]), ...mapState("feature", ["features"]), ...mapState(["last_comments", "user"]), DJANGO_BASE_URL: function () { return this.$store.state.configuration.VUE_APP_DJANGO_BASE; }, last_features: function () { // * limit to last five element of array (looks sorted chronologically, but not sure...) return this.$store.state.feature.features.slice(-5); }, }, methods: { refreshId() { return "?ver=" + Math.random(); }, toNewFeatureType() { this.$router.push({ name: "ajouter-type-signalement", params: { geojson: this.geojsonImport, fileToImport: this.fileToImport, }, }); }, onFileChange(e) { var files = e.target.files || e.dataTransfer.files; if (!files.length) return; this.fileToImport = files[0]; // TODO : VALIDATION IF FILE IS JSON if (this.fileToImport.size > 0) { const fr = new FileReader(); fr.onload = (e) => { this.geojsonImport = JSON.parse(e.target.result); }; fr.readAsText(this.fileToImport); //* stock filename to import features afterward this.$store.commit( "feature_type/SET_FILE_TO_IMPORT", this.fileToImport ); } }, subsribeProject() { this.$store.state.configuration.VUE_APP_DJANGO_API_BASE; projectAPI .subscribeProject({ suscribe: !this.is_suscriber, projectSlug: this.$route.params.slug, }) .then((data) => (this.is_suscriber = data.is_suscriber)); }, }, created() { this.$store.dispatch("GET_PROJECT_INFO", this.slug); projectAPI .getProjectSubscription({ projectSlug: this.$route.params.slug }) .then((data) => (this.is_suscriber = data.is_suscriber)); }, mounted() { if (this.project) { this.$store.dispatch("map/INITIATE_MAP"); const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`; let self = this; axios .get(url) .then((response) => { const features = response.data.features; const featureGroup = mapUtil.addFeatures(features); if (featureGroup && featureGroup.getLayers().length > 0) { mapUtil.getMap().fitBounds(featureGroup.getBounds()); self.$store.commit("map/SET_GEOJSON_FEATURES", features); } }) .catch((error) => { throw error; }); } if (this.message) { this.tempMessage = this.message; document .getElementById("message") .scrollIntoView({ block: "end", inline: "nearest" }); setTimeout(() => { //* hide message after 5 seconds this.tempMessage = null; }, 5000); } }, }; </script> <style> @import "../../assets/resources/semantic-ui-2.4.2/semantic.min.css"; #map { width: 100%; height: 100%; min-height: 250px; } .list-image-type { margin-right: 5px; height: 25px; vertical-align: bottom; } /* // ! missing style in semantic.min.css, je ne comprends pas comment... */ .ui.right.floated.button { float: right; margin: 0 0 0 1em; } .nouveau-type-signalement { padding-top: 1em; } #button-import { padding-top: 0.5em; } .fullwidth { width: 100%; } </style>