diff --git a/src/components/Dropdown.vue b/src/components/Dropdown.vue index 9f2d725faa84e17bd4f4e900c5d6e984e2254dc9..0afade8d86427718d0918404aee33febad0d0c90 100644 --- a/src/components/Dropdown.vue +++ b/src/components/Dropdown.vue @@ -112,7 +112,9 @@ export default { setTimeout(() => { this.isOpen = false; }, 0); - this.$emit("update:selection", this.filteredOptions[index]); + if (this.filteredOptions) { + this.$emit("update:selection", this.filteredOptions[index]); + } this.input = ""; }, diff --git a/src/components/feature/FeatureListTable.vue b/src/components/feature/FeatureListTable.vue index 8a3ca07c27ba26b86c79e10ce882363548e73ece..47b2d1fd5507d95ace501dd54e5e480c6d3db262 100644 --- a/src/components/feature/FeatureListTable.vue +++ b/src/components/feature/FeatureListTable.vue @@ -283,7 +283,7 @@ export default { }, nbPages() { - let N = Math.round( + let N = Math.ceil( this.filteredFeatures.length / this.pagination.pagesize ); const arr = [...Array(N).keys()].map(function (x) { diff --git a/src/store/modules/feature.js b/src/store/modules/feature.js index d5ca0c3b445e338625a667ed2fa716824bd23325..ecf1c64799bf5daed16e1c3072f4b5c8a2a0645d 100644 --- a/src/store/modules/feature.js +++ b/src/store/modules/feature.js @@ -24,6 +24,10 @@ const feature = { name: "Brouillon", value: "draft", }, + { + name: "En attente de publication", + value: "pending", + }, { name: "Publié", value: "published", @@ -32,10 +36,6 @@ const feature = { name: "Archivé", value: "archived", }, - { - name: "En attente de publication", - value: "pending", - }, ], }, mutations: { diff --git a/src/views/feature/Feature_detail.vue b/src/views/feature/Feature_detail.vue index e69a936c8d90a3c5275f39d7502d6898329e4388..b31ae747f01c276f33016dda3ff41ec61096753f 100644 --- a/src/views/feature/Feature_detail.vue +++ b/src/views/feature/Feature_detail.vue @@ -39,7 +39,7 @@ </router-link> <!-- (permissions && permissions.can_delete_feature) || --> <a - v-if="isFeatureCreator" + v-if="isFeatureCreator || permissions.is_project_super_contributor" @click="isCanceling = true" id="feature-delete" class="ui button button-hover-red" diff --git a/src/views/feature/Feature_edit.vue b/src/views/feature/Feature_edit.vue index fd9972f9e1b65910f27ec7944f6862bf4ed4f963..8d8bd8066691082a3cbfabad9028a990c8a8f1ef 100644 --- a/src/views/feature/Feature_edit.vue +++ b/src/views/feature/Feature_edit.vue @@ -409,11 +409,16 @@ export default { ? this.feature.creator === this.user.id //* prevent undefined feature : false; //* si le contributeur est l'auteur du signalement if ( - //* si admin ou modérateur, statuts toujours disponible : Brouillon, Publié, Archivé + //* si admin, modérateur ou super contributeur, statuts toujours disponible: Brouillon, Publié, Archivé userStatus === "Modérateur" || - userStatus === "Administrateur projet" + userStatus === "Administrateur projet" || + (userStatus === "Super Contributeur" && !isModerate) ) { return this.statusChoices.filter((el) => el.value !== "pending"); + } else if (userStatus === "Super Contributeur" && isModerate) { + return this.statusChoices.filter( + (el) => el.value === "draft" || el.value === "pending" + ); } else if (userStatus === "Contributeur") { //* cas particuliers du contributeur if ( @@ -692,10 +697,11 @@ export default { if ( this.project.moderation && !this.permissions.is_project_administrator && - !this.permissions.is_project_moderator + !this.permissions.is_project_moderator && + this.feature.status === "pending" //* allow (super)contributor to change status to pending ) { - this.form.status.value = {"name":"Brouillon","value":"draft"}; - this.updateStore() + this.form.status.value = { name: "Brouillon", value: "draft" }; + this.updateStore(); } this.$store.dispatch("feature/SEND_FEATURE", this.currentRouteName); } diff --git a/src/views/feature/Feature_list.vue b/src/views/feature/Feature_list.vue index 6f5685f5dc42b43e169e726a2e2a545a35be2244..a9f9fb4a9e67a2fe57881e470d1cbdbd2dda80cf 100644 --- a/src/views/feature/Feature_list.vue +++ b/src/views/feature/Feature_list.vue @@ -75,12 +75,13 @@ <div v-if="checkedFeatures.length" + @click="modalAllDelete" class="ui button compact button-hover-red margin-left-25" data-tooltip="Effacer tous les types de signalements sélectionnés" data-position="left center" data-variation="mini" > - <i class="grey trash fitted icon" @click="modalAllDelete"></i> + <i class="grey trash fitted icon"></i> </div> </div> </div> @@ -170,7 +171,7 @@ <button @click="deleteAllFeatureSelection" type="button" - class="ui red compact fluid button" + class="ui red compact fluid button no-margin" > Confirmer la suppression </button> @@ -302,13 +303,18 @@ export default { this.modalAllDeleteOpen = !this.modalAllDeleteOpen; }, - deleteFeature(feature) { - const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/${feature.feature_id}`; + deleteFeature(feature_id) { + const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/${feature_id}/?project__slug=${this.project.slug}`; axios .delete(url, {}) .then(() => { if (!this.modalAllDeleteOpen) { - this.$router.go(); + this.$store + .dispatch("feature/GET_PROJECT_FEATURES", this.project.slug) + .then(() => { + this.getNloadGeojsonFeatures(); + this.checkedFeatures.splice(feature_id); + }); } }) .catch(() => { @@ -320,7 +326,7 @@ export default { let feature = {}; this.checkedFeatures.forEach((feature_id) => { feature = { feature_id: feature_id }; - this.deleteFeature(feature); + this.deleteFeature(feature.feature_id); }); this.modalAllDelete(); }, @@ -329,27 +335,27 @@ export default { if (this.featureGroup) { const features = this.filteredFeatures; this.featureGroup.clearLayers(); - this.featureGroup = mapUtil.addFeatures(features, {},false,this.$store.state.feature_type.feature_types); + this.featureGroup = mapUtil.addFeatures( + features, + {}, + false, + this.$store.state.feature_type.feature_types + ); mapUtil.getMap().invalidateSize(); - mapUtil.getMap()._onResize();// force refresh for vector tiles - - if(this.featureGroup.getLayers().length>0){ + mapUtil.getMap()._onResize(); // force refresh for vector tiles + + if (this.featureGroup.getLayers().length > 0) { mapUtil .getMap() .fitBounds(this.featureGroup.getBounds(), { padding: [25, 25] }); + } else { + mapUtil.getMap().zoomOut(1); } - else{ - mapUtil - .getMap() - .zoomOut(1); - } - - } }, initMap() { - console.log(this) + console.log(this); this.zoom = this.$route.query.zoom || ""; this.lat = this.$route.query.lat || ""; this.lng = this.$route.query.lng || ""; @@ -373,47 +379,60 @@ export default { }); // --------- End sidebar events ---------- - const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`; - this.$store.commit( - "DISPLAY_LOADER", - "Récupération des signalements en cours..." - ); - axios - .get(url) - .then((response) => { - if (response.status === 200 && response.data.features.length > 0) { - this.loadFeatures(response.data.features); - } - this.$store.commit("DISCARD_LOADER"); - }) - .catch((error) => { - this.$store.commit("DISCARD_LOADER"); - throw error; - }); - + this.getNloadGeojsonFeatures(); setTimeout( function () { - let project_id=this.$route.params.slug.split('-')[0]; + let project_id = this.$route.params.slug.split("-")[0]; const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`; - mapUtil.addVectorTileLayer(mvtUrl,this.$route.params.slug,this.$store.state.feature_type.feature_types,this.form); + mapUtil.addVectorTileLayer( + mvtUrl, + this.$route.params.slug, + this.$store.state.feature_type.feature_types, + this.form + ); mapUtil.addGeocoders(this.$store.state.configuration); }.bind(this), 1000 ); }, + getNloadGeojsonFeatures() { + const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`; + this.$store.commit( + "DISPLAY_LOADER", + "Récupération des signalements en cours..." + ); + axios + .get(url) + .then((response) => { + if (response.status === 200 && response.data.features.length > 0) { + this.loadFeatures(response.data.features); + } + this.$store.commit("DISCARD_LOADER"); + }) + .catch((error) => { + this.$store.commit("DISCARD_LOADER"); + throw error; + }); + }, + loadFeatures(features) { this.geojsonFeatures = features; const urlParams = new URLSearchParams(window.location.search); const featureType = urlParams.get("feature_type"); const featureStatus = urlParams.get("status"); const featureTitle = urlParams.get("title"); - this.featureGroup = mapUtil.addFeatures(this.geojsonFeatures, { - featureType, - featureStatus, - featureTitle, - },false,this.$store.state.feature_type.feature_types); + this.featureGroup = mapUtil.addFeatures( + this.geojsonFeatures, + { + featureType, + featureStatus, + featureTitle, + }, + false, + this.$store.state.feature_type.feature_types + ); // Fit the map to bound only if no initial zoom and center are defined if ( (this.lat === "" || this.lng === "" || this.zoom === "") && diff --git a/src/views/project/Project_detail.vue b/src/views/project/Project_detail.vue index 7aece1948b948eb5fe53410ae3d20aab1ad8b4b6..ee2ea87f07ed1227a706d0385ace4f0890eda9c6 100644 --- a/src/views/project/Project_detail.vue +++ b/src/views/project/Project_detail.vue @@ -52,19 +52,24 @@ <h1 class="ui header"> <div class="content"> {{ project.title }} - <div v-if="arraysOffline.length>0">{{arraysOffline.length}} modifications en attente + <div v-if="arraysOffline.length > 0"> + {{ arraysOffline.length }} modifications en attente <button - :disabled="isOffline()" - @click="sendOfflineFeatures()" - class="ui fluid teal icon button" - > - <i class="upload icon"></i> Envoyer au serveur - </button> - + :disabled="isOffline()" + @click="sendOfflineFeatures()" + class="ui fluid teal icon button" + > + <i class="upload icon"></i> Envoyer au serveur + </button> </div> <div class="ui icon right floated compact buttons"> <a - v-if="user && permissions && permissions.can_view_project && isOffline()!=true" + v-if=" + user && + permissions && + permissions.can_view_project && + isOffline() != true + " id="subscribe-button" class="ui button button-hover-green" data-tooltip="S'abonner au projet" @@ -75,7 +80,11 @@ <i class="inverted grey envelope icon"></i> </a> <router-link - v-if="permissions && permissions.can_update_project && isOffline()!=true" + v-if=" + permissions && + permissions.can_update_project && + isOffline() != true + " :to="{ name: 'project_edit', params: { slug: project.slug } }" class="ui button button-hover-orange" data-tooltip="Modifier le projet" @@ -166,7 +175,8 @@ v-if=" project && permissions && - permissions.can_create_feature_type && isOffline()!=true + permissions.can_create_feature_type && + isOffline() != true " class=" ui @@ -192,7 +202,8 @@ project && type.is_editable && permissions && - permissions.can_create_feature_type && isOffline()!=true + permissions.can_create_feature_type && + isOffline() != true " class=" ui @@ -218,7 +229,11 @@ <div class="nouveau-type-signalement"> <router-link - v-if="permissions && permissions.can_update_project && isOffline()!=true" + v-if=" + permissions && + permissions.can_update_project && + isOffline() != true + " :to="{ name: 'ajouter-type-signalement', params: { slug: project.slug }, @@ -230,7 +245,11 @@ </div> <div class="nouveau-type-signalement"> <a - v-if="permissions && permissions.can_update_project && isOffline()!=true" + v-if=" + permissions && + permissions.can_update_project && + isOffline() != true + " class=" ui compact @@ -531,67 +550,75 @@ export default { refreshId() { return "?ver=" + Math.random(); }, - isOffline(){ - return navigator.onLine==false; + isOffline() { + return navigator.onLine == false; }, - checkForOfflineFeature(){ - let arraysOffline=[]; - let localStorageArray=localStorage.getItem("geocontrib_offline"); - if(localStorageArray){ - arraysOffline=JSON.parse(localStorageArray); - this.arraysOffline=arraysOffline.filter(x=>x.project==this.project.slug); + checkForOfflineFeature() { + let arraysOffline = []; + let localStorageArray = localStorage.getItem("geocontrib_offline"); + if (localStorageArray) { + arraysOffline = JSON.parse(localStorageArray); + this.arraysOffline = arraysOffline.filter( + (x) => x.project == this.project.slug + ); } }, - sendOfflineFeatures(){ + sendOfflineFeatures() { var promises = []; - this.arraysOffline.forEach((feature, index, object)=>{ + this.arraysOffline.forEach((feature, index, object) => { console.log(feature); - if(feature.type=='post') { + if (feature.type == "post") { promises.push( - axios - .post(`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/`, feature.geojson) - .then((response) => { - console.log(response) - if (response.status === 201 && response.data) { - object.splice(index, 1); - } - }) - .catch((error) => { - console.log(error); - })); - } - else if(feature.type=='put') { + axios + .post( + `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/`, + feature.geojson + ) + .then((response) => { + console.log(response); + if (response.status === 201 && response.data) { + object.splice(index, 1); + } + }) + .catch((error) => { + console.log(error); + }) + ); + } else if (feature.type == "put") { promises.push( - axios - .put(`${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/${feature.featureId}`, feature.geojson) - .then((response) => { - console.log(response) - if (response.status === 200 && response.data) { - object.splice(index, 1); - } - }) - .catch((error) => { - console.log(error); - })); + axios + .put( + `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features/${feature.featureId}`, + feature.geojson + ) + .then((response) => { + console.log(response); + if (response.status === 200 && response.data) { + object.splice(index, 1); + } + }) + .catch((error) => { + console.log(error); + }) + ); } }); Promise.all(promises).then(() => { - this.updateLocalStorage(); - window.location.reload(); - } - - ); - + this.updateLocalStorage(); + window.location.reload(); + }); }, - updateLocalStorage(){ - let arraysOffline=[]; - let localStorageArray=localStorage.getItem("geocontrib_offline"); - if(localStorageArray){ - arraysOffline=JSON.parse(localStorageArray); + updateLocalStorage() { + let arraysOffline = []; + let localStorageArray = localStorage.getItem("geocontrib_offline"); + if (localStorageArray) { + arraysOffline = JSON.parse(localStorageArray); } - let arraysOfflineOtherProject = arraysOffline.filter(x=>x.project!=this.project.slug); - arraysOffline=arraysOfflineOtherProject.concat(this.arraysOffline); - localStorage.setItem("geocontrib_offline",JSON.stringify(arraysOffline)); + let arraysOfflineOtherProject = arraysOffline.filter( + (x) => x.project != this.project.slug + ); + arraysOffline = arraysOfflineOtherProject.concat(this.arraysOffline); + localStorage.setItem("geocontrib_offline", JSON.stringify(arraysOffline)); }, toNewFeatureType() { this.$router.push({ @@ -642,54 +669,65 @@ export default { setTimeout(() => (this.infoMessage = ""), 3000); }); }, - initMap(){ - if (this.project && this.permissions.can_view_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; - this.checkForOfflineFeature(); - let project_id=this.$route.params.slug.split('-')[0]; - const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`; - mapUtil.addVectorTileLayer(mvtUrl,this.$route.params.slug,this.$store.state.feature_type.feature_types); - axios - .get(url) - .then((response) => { - let features = response.data.features; - self.arraysOffline.forEach(x=>x.geojson.properties.color="red"); - features=response.data.features.concat(self.arraysOffline.map(x=>x.geojson)); - const featureGroup = mapUtil.addFeatures(features,{},false,this.$store.state.feature_type.feature_types); + initMap() { + if (this.project && this.permissions.can_view_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; + this.checkForOfflineFeature(); + let project_id = this.$route.params.slug.split("-")[0]; + const mvtUrl = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`; + mapUtil.addVectorTileLayer( + mvtUrl, + this.$route.params.slug, + this.$store.state.feature_type.feature_types + ); + axios + .get(url) + .then((response) => { + let features = response.data.features; + self.arraysOffline.forEach( + (x) => (x.geojson.properties.color = "red") + ); + features = response.data.features.concat( + self.arraysOffline.map((x) => x.geojson) + ); + const featureGroup = mapUtil.addFeatures( + features, + {}, + false, + this.$store.state.feature_type.feature_types + ); - if (featureGroup && featureGroup.getLayers().length > 0) { - mapUtil - .getMap() - .fitBounds(featureGroup.getBounds(), { padding: [25, 25] }); - this.$store.commit("map/SET_GEOJSON_FEATURES", features); - } else { - this.$store.commit("map/SET_GEOJSON_FEATURES", []); - } - }) - .catch((error) => { - throw error; - }); - - } - }, + if (featureGroup && featureGroup.getLayers().length > 0) { + mapUtil + .getMap() + .fitBounds(featureGroup.getBounds(), { padding: [25, 25] }); + this.$store.commit("map/SET_GEOJSON_FEATURES", features); + } else { + this.$store.commit("map/SET_GEOJSON_FEATURES", []); + } + }) + .catch((error) => { + throw error; + }); + } + }, }, created() { - - if (this.user) { projectAPI .getProjectSubscription({ projectSlug: this.$route.params.slug }) .then((data) => (this.is_suscriber = data.is_suscriber)); } }, - mounted() { - let self=this; - this.$store.dispatch("GET_PROJECT_INFO", this.slug).then(setTimeout(self.initMap,1000)); + let self = this; + this.$store + .dispatch("GET_PROJECT_INFO", this.slug) + .then(setTimeout(self.initMap, 1000)); if (this.message) { this.tempMessage = this.message; document diff --git a/src/views/project/Project_edit.vue b/src/views/project/Project_edit.vue index 7bbf384af7270a9e3657214bde60d19468d5887d..70862005c0a224ce231aeafccb58a170dbf9f825 100644 --- a/src/views/project/Project_edit.vue +++ b/src/views/project/Project_edit.vue @@ -484,13 +484,13 @@ export default { this.form.access_level_pub_feature = { name: this.project.access_level_pub_feature, value: this.levelPermissions.find( - (el) => (el.name = this.project.access_level_pub_feature) + (el) => (el.name === this.project.access_level_pub_feature) ).value, }; this.form.access_level_arch_feature = { name: this.project.access_level_arch_feature, value: this.levelPermissions.find( - (el) => (el.name = this.project.access_level_arch_feature) + (el) => (el.name === this.project.access_level_arch_feature) ).value, }; } diff --git a/src/views/project/Project_members.vue b/src/views/project/Project_members.vue index 0619b21259e64bddb5d469f366e3ee215bcf48ee..ed96cae6d4cc9b97abb62ab97c64a1a41d9c63a0 100644 --- a/src/views/project/Project_members.vue +++ b/src/views/project/Project_members.vue @@ -127,6 +127,7 @@ export default { options: [ { name: "Utilisateur connecté", value: "logged_user" }, { name: "Contributeur", value: "contributor" }, + { name: "Super Contributeur", value: "super_contributor" }, { name: "Modérateur", value: "moderator" }, { name: "Administrateur projet", value: "admin" }, ],