diff --git a/src/components/feature/FeatureListTable.vue b/src/components/feature/FeatureListTable.vue index 2787cb177a7096722f862dfe21b194c5bcf96216..dfba1b87b37cd5777d577bf6c5c9d0bc09068156 100644 --- a/src/components/feature/FeatureListTable.vue +++ b/src/components/feature/FeatureListTable.vue @@ -237,7 +237,7 @@ export default { name: "FeatureListTable", props: [ - "geojsonFeatures", + "paginatedFeatures", "checkedFeatures", "featuresCount", "pagination", @@ -262,7 +262,7 @@ export default { }, sortedFeatures() { - let sortedFeatures = [...this.geojsonFeatures]; + let sortedFeatures = [...this.paginatedFeatures]; // Ajout du tri if (this.sort.column !== "") { sortedFeatures = sortedFeatures.sort((a, b) => { diff --git a/src/services/feature-api.js b/src/services/feature-api.js index e9a02215c3a9f855c376e8ccf357d722fd63355d..3618c8687cd360acd707e991b6f8bd050e5768c7 100644 --- a/src/services/feature-api.js +++ b/src/services/feature-api.js @@ -1,9 +1,39 @@ -import axios from 'axios'; +import axios from "@/axios-client.js"; import store from '../store' const baseUrl = store.state.configuration.VUE_APP_DJANGO_API_BASE; const featureAPI = { + async getFeaturesBbox(project_slug, queryParams) { + const response = await axios.get( + `${baseUrl}projects/${project_slug}/feature-bbox/${queryParams ? '?' + queryParams : ""}` + ); + if ( + response.status === 200 && + response.data + ) { + const bbox = response.data; + return [ + [bbox[2], bbox[3]], + [bbox[0], bbox[1]], + ]; + } else { + return null; + } + }, + + async getPaginatedFeatures(url) { + const response = await axios.get(url); + if ( + response.status === 200 && + response.data + ) { + return response.data; + } else { + return null; + } + }, + async getFeatureEvents(featureId) { const response = await axios.get( `${baseUrl}features/${featureId}/events/` diff --git a/src/views/feature/Feature_list.vue b/src/views/feature/Feature_list.vue index 1bb24d0fbc731c81422a92f8ed244ce03e499798..408091df72d8955bc4a332ddd4fef83f14128038 100644 --- a/src/views/feature/Feature_list.vue +++ b/src/views/feature/Feature_list.vue @@ -1,6 +1,5 @@ <template> <div class="fourteen wide column"> - <div id="feature-list-container" class="ui grid mobile-column"> <div class="four wide column mobile-fullwidth"> <h1>Signalements</h1> @@ -16,7 +15,7 @@ ><i class="map fitted icon"></i ></a> <a - @click="showTable" + @click="showMap = false" :class="['item no-margin', { active: !showMap }]" data-tab="list" data-tooltip="Liste" @@ -24,10 +23,10 @@ ></a> <div class="item"> <h4> - <!-- {{ featuresCount }} signalement{{ featuresCount > 1 ? "s" : "" }} --> - {{ filteredFeatures.length }} signalement{{ + {{ featuresCount }} signalement{{ featuresCount > 1 ? "s" : "" }} + <!-- {{ filteredFeatures.length }} signalement{{ filteredFeatures.length > 1 ? "s" : "" - }} + }} --> </h4> </div> @@ -83,12 +82,12 @@ </div> </div> - <form id="form-filters" class="ui form grid" action="" method="get"> + <section id="form-filters" class="ui form grid"> <div class="field wide four column no-margin-mobile"> <label>Type</label> <Dropdown v-on:update:selection="updateTypeFeatures" - :options="form.type.choices" + :options="featureTypeChoices" :selected="form.type.selected" :selection.sync="form.type.selected" :search="true" @@ -116,12 +115,12 @@ type="text" name="title" v-model="form.title" - @input="fetchPagedFeatures" + v-on:keyup.enter="fetchPagedFeatures" /> <button - type="button" - class="ui teal icon button" + @click="fetchPagedFeatures" id="submit-search" + class="ui teal icon button" > <i class="search icon"></i> </button> @@ -132,7 +131,7 @@ <input type="hidden" name="zoom" v-model="zoom" /> <input type="hidden" name="lat" v-model="lat" /> <input type="hidden" name="lng" v-model="lng" /> - </form> + </section> <div v-show="showMap" class="ui tab active map-container" data-tab="map"> <div id="map" ref="map"></div> @@ -142,7 +141,7 @@ <FeatureListTable v-show="!showMap" v-on:update:page="handlePageChange" - :geojsonFeatures="geojsonFeaturesPaginated" + :paginatedFeatures="paginatedFeatures" :checkedFeatures.sync="checkedFeatures" :featuresCount="featuresCount" :pagination="pagination" @@ -185,17 +184,12 @@ <script> import { mapGetters, mapState } from "vuex"; import { mapUtil } from "@/assets/js/map-util.js"; +import featureAPI from "@/services/feature-api"; import SidebarLayers from "@/components/map-layers/SidebarLayers"; import FeatureListTable from "@/components/feature/FeatureListTable"; import Dropdown from "@/components/Dropdown.vue"; import axios from "@/axios-client.js"; -// axios.defaults.headers.common['X-CSRFToken'] = (name => { -// var re = new RegExp(name + "=([^;]+)"); -// var value = re.exec(document.cookie); -// return (value !== null) ? unescape(value[1]) : null; -// })('csrftoken'); - export default { name: "Feature_list", @@ -239,30 +233,23 @@ export default { }, title: null, }, - - geojsonFeatures: [], - geojsonFeaturesPaginated: [], + paginatedFeatures: [], baseUrl: this.$store.state.configuration.BASE_URL, map: null, zoom: null, lat: null, lng: null, - //limit: 15, - //offset: 0, featuresCount: 0, - filterType: null, - filterStatus: null, + next: null, + previous: null, pagination: { currentPage: 1, pagesize: 15, start: 0, end: 15, }, - previous: null, - next: null, showMap: true, showAddFeature: false, - paginatedFeaturesDone: true, }; }, @@ -283,6 +270,10 @@ export default { ); }, + featureTypeChoices() { + return this.feature_types.map((el) => el.title); + }, + pageNumbers() { const totalPages = Math.ceil( this.featuresCount / this.pagination.pagesize @@ -292,51 +283,9 @@ export default { return pageNumb; }); }, - - filteredFeatures() { - let results = this.geojsonFeatures; - if (this.form.type.selected) { - results = results.filter( - (el) => el.properties.feature_type.title === this.form.type.selected - ); - } - if (this.form.status.selected.value) { - console.log("filter by" + this.form.status.selected.value); - results = results.filter( - (el) => el.properties.status.value === this.form.status.selected.value - ); - } - if (this.form.title) { - results = results.filter((el) => { - if (el.properties.title) { - return el.properties.title - .toLowerCase() - .includes(this.form.title.toLowerCase()); - } else - return el.id.toLowerCase().includes(this.form.title.toLowerCase()); - }); - } - return results; - }, - }, - - watch: { - filteredFeatures(newValue, oldValue) { - if (newValue && newValue !== oldValue) { - this.onFilterChange(); - } - }, }, methods: { - showTable() { - if (this.paginatedFeaturesDone) { - this.fetchPagedFeatures(); - this.paginatedFeaturesDone = false; - } - this.showMap = false; - }, - modalAllDelete() { this.modalAllDeleteOpen = !this.modalAllDeleteOpen; }, @@ -373,28 +322,12 @@ export default { }, onFilterChange() { - if (this.featureGroup) { - const features = this.filteredFeatures; - this.featureGroup.clearLayers(); - this.featureGroup = mapUtil.addFeatures( - features, - {}, - true, - this.feature_types - ); + if (mapUtil.getMap()) { mapUtil.getMap().invalidateSize(); mapUtil.getMap()._onResize(); // force refresh for vector tiles - if(window.layerMVT) { + if (window.layerMVT) { window.layerMVT.redraw(); } - - if (this.featureGroup.getLayers().length > 0) { - mapUtil - .getMap() - .fitBounds(this.featureGroup.getBounds(), { padding: [25, 25] }); - } else { - mapUtil.getMap().zoomOut(1); - } } }, @@ -416,6 +349,8 @@ export default { mapDefaultViewZoom, }); + this.getBbox2FIt(); + 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 @@ -423,8 +358,6 @@ export default { }); // --------- End sidebar events ---------- - //this.fetchPagedFeatures(); - this.getNloadGeojsonFeatures(); setTimeout(() => { const project_id = this.$route.params.slug.split("-")[0]; @@ -439,59 +372,16 @@ export default { }, 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.geojsonFeatures = response.data.features; - this.loadFeatures(); + getBbox2FIt(queryParams) { + featureAPI + .getFeaturesBbox(this.project.slug, queryParams) + .then((bbox) => { + if (bbox) { + mapUtil.getMap().fitBounds(bbox, { padding: [25, 25] }); } - this.$store.commit("DISCARD_LOADER"); - }) - .catch((error) => { - this.$store.commit("DISCARD_LOADER"); - throw error; }); }, - loadFeatures() { - 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, - }, - true, - this.feature_types - ); - // Fit the map to bound only if no initial zoom and center are defined - if ( - (this.lat === "" || this.lng === "" || this.zoom === "") && - this.geojsonFeatures.length > 0 - ) { - mapUtil - .getMap() - .fitBounds(this.featureGroup.getBounds(), { padding: [25, 25] }); - } - this.form.type.choices = [ - //* converting Set to an Array with spread "..." - ...new Set( - this.geojsonFeatures.map((el) => el.properties.feature_type.title) - ), //* use Set to eliminate duplicate values - ]; - }, - //* Paginated Features for table *// getFeatureTypeSlug(title) { const featureType = this.feature_types.find((el) => el.title === title); @@ -549,7 +439,7 @@ export default { if (this.form.title) { params += `&title=${this.form.title}`; } - + this.getBbox2FIt(params); return params; }, @@ -564,8 +454,7 @@ export default { }, fetchPagedFeatures(params) { - // this.onFilterChange(); //* temporary, use paginated event to watch change in filters, to modify geojson on map - //* replace function calls in watcher and on input + this.onFilterChange(); //* temporary, use paginated event to watch change in filters, to modify geojson on map let url = `${this.API_BASE_URL}projects/${this.$route.params.slug}/feature-paginated/?output=geojson&limit=${this.pagination.pagesize}&offset=${this.pagination.start}`; if (params) { @@ -575,8 +464,7 @@ export default { url += filterParams; } else { //console.error("ONLY FOR DEV !!!!!!!!!!!!!"); - //params = params.replace("8000", "8010"); - //console.log(url); + //params = params.replace("8000", "8010"); //* for dev uncomment to use proxy link url = params; } } @@ -585,25 +473,15 @@ export default { "DISPLAY_LOADER", "Récupération des signalements en cours..." ); - axios - .get(url) - .then((response) => { - if (response.status === 200) { - this.featuresCount = response.data.count; - this.previous = response.data.previous; - this.next = response.data.next; - this.geojsonFeaturesPaginated = response.data.results.features; - //if (response.data.results.features.length > 0) { - //this.loadFeatures(); - //this.onFilterChange(); - //} - } - this.$store.commit("DISCARD_LOADER"); - }) - .catch((error) => { - this.$store.commit("DISCARD_LOADER"); - throw error; - }); + featureAPI.getPaginatedFeatures(url).then((data) => { + if (data) { + this.featuresCount = data.count; + this.previous = data.previous; + this.next = data.next; + this.paginatedFeatures = data.results.features; + } + this.$store.commit("DISCARD_LOADER"); + }); }, //* Pagination for table *// @@ -656,12 +534,11 @@ export default { // Chargements des features et infos projet en cas d'arrivée directe sur la page ou de refresh this.$store .dispatch("GET_PROJECT_INFO", this.$route.params.slug) - .then(() => { - this.initMap(); - }); + .then(() => this.initMap()); } else { this.initMap(); } + this.fetchPagedFeatures(); }, destroyed() { diff --git a/src/views/project/Project_detail.vue b/src/views/project/Project_detail.vue index 3d5a04b4f3a5cae82d95a2a01126765ab6c7fff6..a521510dafccaec62c0d38b68930b86e4f4d8773 100644 --- a/src/views/project/Project_detail.vue +++ b/src/views/project/Project_detail.vue @@ -406,7 +406,8 @@ > <div class="content"> <div> - <router-link :to="getRouteUrl(item.related_feature.feature_url)" + <router-link + :to="getRouteUrl(item.related_feature.feature_url)" >"{{ item.comment }}"</router-link > </div> @@ -550,14 +551,9 @@ import frag from "vue-frag"; import { mapUtil } from "@/assets/js/map-util.js"; import { mapGetters, mapState } from "vuex"; import projectAPI from "@/services/project-api"; +import featureAPI from "@/services/feature-api"; -import axios from '@/axios-client.js'; - -// axios.defaults.headers.common['X-CSRFToken'] = (name => { -// var re = new RegExp(name + "=([^;]+)"); -// var value = re.exec(document.cookie); -// return (value !== null) ? unescape(value[1]) : null; -// })('csrftoken'); +import axios from "@/axios-client.js"; export default { name: "Project_details", @@ -617,8 +613,8 @@ export default { refreshId() { return "?ver=" + Math.random(); }, - getRouteUrl(url){ - return '/'+url.replace(this.$store.state.configuration.BASE_URL,''); // remove duplicate /geocontrib + getRouteUrl(url) { + return "/" + url.replace(this.$store.state.configuration.BASE_URL, ""); // remove duplicate /geocontrib }, isOffline() { return navigator.onLine === false; @@ -755,7 +751,6 @@ export default { initMap() { if (this.project && this.permissions.can_view_project) { this.$store.dispatch("map/INITIATE_MAP", this.$refs.map); - const url = `${this.API_BASE_URL}projects/${this.$route.params.slug}/feature/?output=geojson`; this.checkForOfflineFeature(); let project_id = this.$route.params.slug.split("-")[0]; const mvtUrl = `${this.API_BASE_URL}features.mvt/?tile={z}/{x}/{y}&project_id=${project_id}`; @@ -764,34 +759,22 @@ export default { this.$route.params.slug, this.$store.state.feature_type.feature_types ); - axios - .get(url) - .then((response) => { - let features = response.data.features; - this.arraysOffline.forEach( - (x) => (x.geojson.properties.color = "red") - ); - features = response.data.features.concat( - this.arraysOffline.map((x) => x.geojson) - ); - const featureGroup = mapUtil.addFeatures( - features, - {}, - true, - 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", []); + + this.arraysOffline.forEach((x) => (x.geojson.properties.color = "red")); + const features = this.arraysOffline.map((x) => x.geojson); + mapUtil.addFeatures( + features, + {}, + true, + this.$store.state.feature_type.feature_types + ); + + featureAPI + .getFeaturesBbox(this.project.slug) + .then((bbox) => { + if (bbox) { + mapUtil.getMap().fitBounds(bbox, { padding: [25, 25] }); } - }) - .catch((error) => { - throw error; }); } }, @@ -807,14 +790,14 @@ export default { }, mounted() { - this.$store.dispatch('GET_PROJECT_INFO', this.slug) - .then(() => { - this.featureTypeLoading = false; - setTimeout(this.initMap, 1000); - }); - this.$store.dispatch('feature/GET_PROJECT_FEATURES', { - project_slug: this.slug - }) + this.$store.dispatch("GET_PROJECT_INFO", this.slug).then(() => { + this.featureTypeLoading = false; + setTimeout(this.initMap, 1000); + }); + this.$store + .dispatch("feature/GET_PROJECT_FEATURES", { + project_slug: this.slug, + }) .then(() => { this.featuresLoading = false; }); @@ -888,4 +871,4 @@ export default { .text-left { text-align: left !important; } -</style> \ No newline at end of file +</style>