diff --git a/src/main.js b/src/main.js index 56bc1b0585e4d9d65e0712b9b6438479597db717..87be60e859f1e0bd80fc3ea91ea87260740da337 100644 --- a/src/main.js +++ b/src/main.js @@ -54,6 +54,7 @@ let onConfigLoaded = function(config){ store.dispatch("GET_USER_LEVEL_PROJECTS"), store.dispatch("map/GET_AVAILABLE_LAYERS"), store.dispatch("GET_USER_LEVEL_PERMISSIONS"), + store.dispatch("GET_LEVELS_PERMISSIONS"), ]).then(axios.spread(function () { new Vue({ router, diff --git a/src/store/index.js b/src/store/index.js index 8ca73167c4c0afe944e3b551f99d243c47b79ab4..bc5b7567f9f6994e4b2bd439d54daea9537e2989 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -46,6 +46,7 @@ export default new Vuex.Store({ SSO_SETTED: false, USER_LEVEL_PROJECTS: null, user_permissions: null, + levelsPermissions: [], messages: [], events: null, loader: { @@ -95,6 +96,9 @@ export default new Vuex.Store({ SET_USER_PERMISSIONS(state, userPermissions) { state.user_permissions = userPermissions; }, + SET_LEVELS_PERMISSIONS(state, levelsPermissions) { + state.levelsPermissions = levelsPermissions; + }, SET_EVENTS(state, events) { state.events = events; }, @@ -280,6 +284,18 @@ export default new Vuex.Store({ throw error; }); }, + GET_LEVELS_PERMISSIONS({ commit }) { + return axios + .get(`${this.state.configuration.VUE_APP_DJANGO_API_BASE}levels-permissions/`) + .then((response) => { + if (response && response.status === 200) { + commit("SET_LEVELS_PERMISSIONS", response.data); + } + }) + .catch((error) => { + throw error; + }); + }, async GET_PROJECT_INFO({ state, commit, dispatch }, slug) { commit("SET_PROJECT_SLUG", slug); diff --git a/src/store/modules/feature.js b/src/store/modules/feature.js index 8d8bd2f237608f89ae580b6ad6af716854d1c795..d4aad9d8d650e87945344403d291a1608648340b 100644 --- a/src/store/modules/feature.js +++ b/src/store/modules/feature.js @@ -16,6 +16,7 @@ const feature = { checkedFeatures: [], extra_form: [], features: [], + features_count: 0, current_feature: [], form: null, linkedFormset: [], @@ -43,6 +44,9 @@ const feature = { SET_FEATURES(state, features) { state.features = features; }, + SET_FEATURES_COUNT(state, features_count) { + state.features_count = features_count; + }, SET_CURRENT_FEATURE(state, feature) { state.current_feature = feature; }, @@ -116,6 +120,7 @@ const feature = { const cancelToken = axios.CancelToken.source(); commit('SET_CANCELLABLE_SEARCH_REQUEST', cancelToken, { root: true }); commit("SET_FEATURES", []); + commit("SET_FEATURES_COUNT", 0); let url = `${rootState.configuration.VUE_APP_DJANGO_API_BASE}projects/${project_slug}/feature/`; if (feature_type__slug) { url = url.concat('', `${url.includes('?') ? '&' : '?'}feature_type__slug=${feature_type__slug}`); @@ -132,6 +137,8 @@ const feature = { if (response.status === 200 && response.data) { const features = response.data.features; commit("SET_FEATURES", features); + const features_count = response.data.count; + commit("SET_FEATURES_COUNT", features_count); //dispatch("map/ADD_FEATURES", null, { root: true }); //todo: should check if map was initiated } return response; @@ -187,6 +194,7 @@ const feature = { message, }, }); + dispatch("GET_ALL_PROJECTS", null, {root:true}) //* & refresh project list }); } @@ -303,6 +311,7 @@ const feature = { }); } + // this.$store.dispatch("GET_ALL_PROJECTS"), //* & refresh project list }, async SEND_ATTACHMENTS({ state, rootState, dispatch }, featureId) { diff --git a/src/views/Index.vue b/src/views/Index.vue index b751999169c005a0a90fc93e2df3ffc4156c7912..33ced862f9a5074b1d1184ce1d75dbb4877b4ecd 100644 --- a/src/views/Index.vue +++ b/src/views/Index.vue @@ -79,7 +79,7 @@ <span data-tooltip="Membres"> {{ project.nb_contributors }} <i class="user icon"></i> </span> - <span data-tooltip="Signalements"> + <span data-tooltip="Signalements publiés"> {{ project.nb_published_features }} <i class="map marker icon" ></i> diff --git a/src/views/My_account.vue b/src/views/My_account.vue index 8967257b25442a46f9958b202ce1d76486e78d01..162eedc20f53ad512982c19a8ff700730f82791a 100644 --- a/src/views/My_account.vue +++ b/src/views/My_account.vue @@ -102,7 +102,7 @@ <span data-tooltip="Membres"> {{ project.nb_contributors }} <i class="user icon"></i> </span> - <span data-tooltip="Signalements"> + <span data-tooltip="Signalements publiés"> {{ project.nb_published_features }} <i class="map marker icon" ></i> diff --git a/src/views/feature_type/Feature_type_detail.vue b/src/views/feature_type/Feature_type_detail.vue index b541bb17995c3a95c1a00ea0290832b00a8f0b8b..d96d84f28f8c36b02a0832de6f96ab43a241bede 100644 --- a/src/views/feature_type/Feature_type_detail.vue +++ b/src/views/feature_type/Feature_type_detail.vue @@ -33,7 +33,7 @@ </div> </div> <div class="value"> - {{ feature_type_features.length }} + {{ features_count }} </div> <div class="label"> Signalement{{ features.length > 1 ? "s" : "" }} @@ -126,14 +126,25 @@ </div> <div class="nine wide column"> <h3 class="ui header">Derniers signalements</h3> - <div - :class="{ active: featuresLoading }" - class="ui inverted dimmer" - > - <div class="ui text loader"> - Récupération des signalements en cours... - </div> + <div + :class="{ active: featuresLoading }" + class="ui inverted dimmer" + > + <div class="ui text loader"> + Récupération des signalements en cours... </div> + </div> + <div + v-if=" + importFeatureTypeData && importFeatureTypeData.length && importFeatureTypeData.some(el => el.status === 'pending') + " + class="ui message info" + > + <p> + Des signalements sont en cours d'import. + Pour suivre le statut de l'import, cliquez sur "Importer des Signalements". + </p> + </div> <div v-for="(feature, index) in lastFeatures" :key="feature.feature_id + index" @@ -239,7 +250,7 @@ export default { computed: { ...mapGetters(["project", "permissions"]), - ...mapState("feature", ["features"]), + ...mapState("feature", ["features", "features_count"]), ...mapState("feature_type", ["feature_types", "importFeatureTypeData"]), structure: function () { if (this.feature_types) { @@ -369,7 +380,8 @@ export default { async setCurrentFeatureTypeSlug(){ const response = await this.$store.dispatch('feature/GET_PROJECT_FEATURES', { - project_slug: this.$route.params.slug + project_slug: this.$route.params.slug, + limit: '5' }) console.log(response) @@ -390,7 +402,8 @@ export default { if (!this.project) { this.$store.dispatch("GET_PROJECT_INFO", this.$route.params.slug); } - this.setCurrentFeatureTypeSlug(); + this.$store.dispatch("feature_type/GET_IMPORTS", this.structure.slug); + this.setCurrentFeatureTypeSlug(); // .then(res => resolve(res)) // .catch(err => reject(err)); this.$store.commit( diff --git a/src/views/feature_type/Feature_type_edit.vue b/src/views/feature_type/Feature_type_edit.vue index bb69a28bbc5351239cb9382aa181bfb36fe91ff4..8cbda5cfbed20f39a08a0e54273f14bf57c79e1f 100644 --- a/src/views/feature_type/Feature_type_edit.vue +++ b/src/views/feature_type/Feature_type_edit.vue @@ -6,6 +6,12 @@ </div> </div> <div class="fourteen wide column"> + <div + :class="{ active: loading }" + class="ui inverted dimmer" + > + <div class="ui loader" /> + </div> <form id="form-type-edit" action="" @@ -183,6 +189,7 @@ export default { data() { return { + loading: false, action: "create", dataKey: 0, error: null, @@ -482,19 +489,29 @@ export default { response.data.detail ); } + this.loading = false; + }) + .catch(() => { + this.loading = false; }); }, async postFeatureTypeThenFeatures() { + this.loading = true; const requestType = this.action === "edit" ? "put" : "post"; if (this.checkForms()) { await this.$store .dispatch("feature_type/SEND_FEATURE_TYPE", requestType) - .then(({ feature_type_slug }) => { - if (feature_type_slug) { - this.postFeatures(feature_type_slug); - } - }); + .then(({ feature_type_slug }) => { + if (feature_type_slug) { + this.postFeatures(feature_type_slug); + } else { + this.loading = false; + } + }) + .catch(() => { + this.loading = false; + }); } }, diff --git a/src/views/project/Project_detail.vue b/src/views/project/Project_detail.vue index 070b25508651a827577bb4dbdc60ef20f16e58ff..a521510dafccaec62c0d38b68930b86e4f4d8773 100644 --- a/src/views/project/Project_detail.vue +++ b/src/views/project/Project_detail.vue @@ -40,7 +40,7 @@ <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"> + <div class="ui basic teal label" data-tooltip="Signalements publiés"> <i class="map marker icon"></i>{{ project.nb_published_features }} </div> <div class="ui basic teal label" data-tooltip="Commentaires"> @@ -115,17 +115,26 @@ Récupération des types de signalements en cours... </div> </div> + <div + :class="{ active: featureTypeImporting }" + class="ui inverted dimmer" + > + <div class="ui text loader"> + Traitement du fichier en cours ... + </div> + </div> <div v-for="(type, index) in feature_types" :key="type.title + '-' + index" class="item" > - <div class="middle aligned content"> + <div class="feature-type-container"> <router-link :to="{ name: 'details-type-signalement', params: { feature_type_slug: type.slug }, }" + class="feature-type-title" > <img v-if="type.geom_type === 'point'" @@ -152,6 +161,7 @@ permissions.can_create_feature && type.is_editable " --> + <div class="middle aligned content"> <router-link v-if=" project && permissions && permissions.can_create_feature @@ -256,6 +266,7 @@ > <i class="inverted grey paint brush alternate icon"></i> </router-link> + </div> </div> </div> <div v-if="feature_types.length === 0"> @@ -576,7 +587,8 @@ export default { is_suscriber: false, tempMessage: null, featureTypeLoading: true, - featuresLoading: true, + featureTypeImporting: false, + featuresLoading: true }; }, @@ -676,6 +688,8 @@ export default { }, toNewFeatureType() { + console.log('prout'); + this.featureTypeImporting = true; this.$router.push({ name: "ajouter-type-signalement", params: { @@ -683,25 +697,36 @@ export default { fileToImport: this.fileToImport, }, }); + this.featureTypeImporting = false; }, onFileChange(e) { + this.featureTypeImporting = true; var files = e.target.files || e.dataTransfer.files; + console.log(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 - ); + try { + fr.onload = (e) => { + this.geojsonImport = JSON.parse(e.target.result); + this.featureTypeImporting = false; + }; + fr.readAsText(this.fileToImport); + //* stock filename to import features afterward + this.$store.commit( + "feature_type/SET_FILE_TO_IMPORT", + this.fileToImport + ); + } catch (err) { + console.error(err); + this.featureTypeImporting = false + } + } else { + this.featureTypeImporting = false; } }, @@ -805,9 +830,31 @@ export default { float: right; margin: 0 0 0 1em; } + +.feature-type-container { + display: flex; + justify-content: space-between; + align-items: center; +} + +.feature-type-container > .middle.aligned.content { + width: 50%; +} + +.feature-type-title { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + line-height: 1.5em; +} + .nouveau-type-signalement { + cursor: pointer; padding-top: 1em; } +.nouveau-type-signalement > a > .ui > .label{ + cursor: pointer; +} #button-import { padding-top: 0.5em; @@ -824,4 +871,4 @@ export default { .text-left { text-align: left !important; } -</style> \ No newline at end of file +</style> diff --git a/src/views/project/Project_edit.vue b/src/views/project/Project_edit.vue index f9784c6a4e5a064f2201ae0b7cc50c30a14cb556..411d78cc48f9f579ce99d9e8c3d42802a23a7bb2 100644 --- a/src/views/project/Project_edit.vue +++ b/src/views/project/Project_edit.vue @@ -194,7 +194,7 @@ import axios from '@/axios-client.js'; import Dropdown from "@/components/Dropdown.vue"; -import { mapGetters } from "vuex"; +import { mapState, mapGetters } from "vuex"; // axios.defaults.headers.common["X-CSRFToken"] = ((name) => { // var re = new RegExp(name + "=([^;]+)"); @@ -213,11 +213,6 @@ export default { return { loading: false, action: "create", - levelPermissions: [ - { name: "Utilisateur anonyme", value: "anonymous" }, - { name: "Utilisateur connecté", value: "logged_user" }, - { name: "Contributeur", value: "contributor" }, - ], fileToImport: { name: "Sélectionner une image ...", size: 0, @@ -255,10 +250,25 @@ export default { }, computed: { + ...mapState([ + "levelsPermissions", + ]), ...mapGetters(["project"]), DJANGO_BASE_URL: function () { return this.$store.state.configuration.VUE_APP_DJANGO_BASE; }, + levelPermissions(){ + let self = this; + let levels = [] + this.levelsPermissions.map(function(item) { + if (item.user_type_id != "super_contributor") + levels.push({ + 'name': self.traslateRoleToFrench(item.user_type_id), + 'value': item.user_type_id, + }) + }); + return levels + } }, methods: { @@ -271,6 +281,14 @@ export default { this.action = "create_from"; } }, + + traslateRoleToFrench(role){ + if (role == "admin") return "Administrateur de projet"; + if (role == "moderator") return "Modérateur"; + if (role == "contributor") return "Contributeur"; + if (role == "logged_user") return "Utilisateur connecté"; + if (role == "anonymous") return "Utilisateur anonyme"; + }, truncate(n, len) { let ext = n.substring(n.lastIndexOf(".") + 1, n.length).toLowerCase();