diff --git a/src/components/feature/FeatureListTable.vue b/src/components/feature/FeatureListTable.vue index dfba1b87b37cd5777d577bf6c5c9d0bc09068156..5817a4fa2ab3f7acb9aadf77cef804e68c27e0be 100644 --- a/src/components/feature/FeatureListTable.vue +++ b/src/components/feature/FeatureListTable.vue @@ -10,33 +10,33 @@ Statut <i :class="{ - down: isSortedAsc('statut'), - up: isSortedDesc('statut'), + down: isSortedAsc('status'), + up: isSortedDesc('status'), }" class="icon sort" - @click="changeSort('statut')" + @click="changeSort('status')" /> </th> <th class="center"> Type <i :class="{ - down: isSortedAsc('type'), - up: isSortedDesc('type'), + down: isSortedAsc('feature_type'), + up: isSortedDesc('feature_type'), }" class="icon sort" - @click="changeSort('type')" + @click="changeSort('feature_type')" /> </th> <th class="center"> Nom <i :class="{ - down: isSortedAsc('nom'), - up: isSortedDesc('nom'), + down: isSortedAsc('title'), + up: isSortedDesc('title'), }" class="icon sort" - @click="changeSort('nom')" + @click="changeSort('title')" /> </th> <th class="center"> @@ -75,7 +75,7 @@ </tr> </thead> <tbody> - <tr v-for="(feature, index) in sortedFeatures" :key="index"> + <tr v-for="(feature, index) in paginatedFeatures" :key="index"> <td class="center"> <div class="ui checkbox" @@ -153,7 +153,6 @@ > </td> <td class="center"> - <!-- |date:'Ymd' --> {{ feature.properties.updated_on }} </td> <td class="center" v-if="user"> @@ -187,7 +186,7 @@ class="dataTables_paginate paging_simple_numbers" > <a - @click="$emit('update:page', 'previous');" + @click="$emit('update:page', 'previous')" id="table-features_previous" :class="[ 'paginate_button previous', @@ -199,8 +198,20 @@ >Précédent</a > <span> + <span v-if="pagination.currentPage >= 5"> + <a + key="page1" + @click="$emit('update:page', 1)" + class="paginate_button" + aria-controls="table-features" + data-dt-idx="1" + tabindex="0" + >{{ 1 }}</a + > + <span class="ellipsis">…</span> + </span> <a - v-for="pageNumber in pageNumbers" + v-for="pageNumber in displayedPageNumbers" :key="'page' + pageNumber" @click="$emit('update:page', pageNumber)" :class="[ @@ -212,8 +223,19 @@ tabindex="0" >{{ pageNumber }}</a > + <span v-if="(lastPageNumber - pagination.currentPage) >= 4"> + <span class="ellipsis">…</span> + <a + :key="'page' + lastPageNumber" + @click="$emit('update:page', lastPageNumber)" + class="paginate_button" + aria-controls="table-features" + data-dt-idx="1" + tabindex="0" + >{{ lastPageNumber }}</a + > + </span> </span> - <a id="table-features_next" :class="[ @@ -223,7 +245,7 @@ aria-controls="table-features" data-dt-idx="7" tabindex="0" - @click="$emit('update:page', 'next');" + @click="$emit('update:page', 'next')" >Suivant</a > </div> @@ -241,18 +263,9 @@ export default { "checkedFeatures", "featuresCount", "pagination", - "pageNumbers", + "sort", ], - data() { - return { - sort: { - column: "", - ascending: true, - }, - }; - }, - computed: { ...mapState(["user"]), ...mapGetters(["project", "permissions"]), @@ -261,50 +274,6 @@ export default { return this.permissions.is_project_administrator; }, - sortedFeatures() { - let sortedFeatures = [...this.paginatedFeatures]; - // Ajout du tri - if (this.sort.column !== "") { - sortedFeatures = sortedFeatures.sort((a, b) => { - let aProp = this.getFeatureDisplayName(a); - let bProp = this.getFeatureDisplayName(b); - if (this.sort.column === "statut") { - aProp = a.properties.status.value; - bProp = b.properties.status.value; - } else if (this.sort.column === "type") { - aProp = a.properties.feature_type.title; - bProp = b.properties.feature_type.title; - } else if (this.sort.column === "updated_on") { - aProp = a.properties.updated_on; - bProp = b.properties.updated_on; - } else if (this.sort.column === "display_creator") { - aProp = a.properties.display_creator; - bProp = b.properties.display_creator; - } - //ascending - if (this.sort.ascending) { - if (aProp < bProp) { - return -1; - } - if (aProp > bProp) { - return 1; - } - return 0; - } else { - //descending - if (aProp < bProp) { - return 1; - } - if (aProp > bProp) { - return -1; - } - return 0; - } - }); - } - return sortedFeatures; - }, - checked: { get() { return this.checkedFeatures; @@ -319,6 +288,36 @@ export default { ? this.featuresCount : this.pagination.end; }, + + pageNumbers() { + const totalPages = Math.ceil( + this.featuresCount / this.pagination.pagesize + ); + return [...Array(totalPages).keys()].map((pageNumb) => { + ++pageNumb; + return pageNumb; + }); + }, + + lastPageNumber() { + return this.pageNumbers.slice(-1)[0]; + }, + + displayedPageNumbers() { + //* si la page courante est inférieur à 5, la liste commence à l'index 0 et on retourne 5 pages + let firstPageInList = 0; + let pagesQuantity = 5; + //* à partir de la 5ième page et jusqu'à la 4ième page avant la fin : n'afficher que 3 page entre les ellipses et la page courante doit être au milieu + if (this.pagination.currentPage >= 5 && !(this.lastPageNumber - this.pagination.currentPage < 4)) { + firstPageInList = this.pagination.currentPage - 2; + pagesQuantity = 3 + } + //* a partir de 4 résultat avant la fin afficher seulement les 5 derniers résultats + if (this.lastPageNumber - this.pagination.currentPage < 4) { + firstPageInList = this.lastPageNumber - 5; + } + return this.pageNumbers.slice(firstPageInList, firstPageInList + pagesQuantity); + }, }, methods: { @@ -342,10 +341,14 @@ export default { changeSort(column) { if (this.sort.column === column) { //changer order - this.sort.ascending = !this.sort.ascending; + this.$emit("update:sort", { + column: this.sort.column, + ascending: !this.sort.ascending, + }); } else { this.sort.column = column; this.sort.ascending = true; + this.$emit("update:sort", { column, ascending: true }); } }, }, @@ -433,6 +436,10 @@ export default { box-shadow: none; } +.dataTables_wrapper .dataTables_paginate .ellipsis { + padding: 0 1em; +} + i.icon.sort:not(.down):not(.up) { color: rgb(220, 220, 220); } diff --git a/src/views/feature/Feature_list.vue b/src/views/feature/Feature_list.vue index 408091df72d8955bc4a332ddd4fef83f14128038..c31ad0879058b2a0a117cd71012851e6df2bb8c3 100644 --- a/src/views/feature/Feature_list.vue +++ b/src/views/feature/Feature_list.vue @@ -86,7 +86,6 @@ <div class="field wide four column no-margin-mobile"> <label>Type</label> <Dropdown - v-on:update:selection="updateTypeFeatures" :options="featureTypeChoices" :selected="form.type.selected" :selection.sync="form.type.selected" @@ -98,7 +97,6 @@ <label>Statut</label> <!-- //* giving an object mapped on key name --> <Dropdown - v-on:update:selection="updateStatusFeatures" :options="statusChoices" :selected="form.status.selected.name" :selection.sync="form.status.selected" @@ -141,11 +139,12 @@ <FeatureListTable v-show="!showMap" v-on:update:page="handlePageChange" + v-on:update:sort="handleSortChange" :paginatedFeatures="paginatedFeatures" :checkedFeatures.sync="checkedFeatures" :featuresCount="featuresCount" :pagination="pagination" - :pageNumbers="pageNumbers" + :sort="sort" /> <!-- MODAL ALL DELETE FEATURE TYPE --> @@ -201,17 +200,12 @@ export default { data() { return { - modalAllDeleteOpen: false, form: { type: { - selected: null, - choices: [], + selected: "", }, status: { - selected: { - name: null, - value: null, - }, + selected: "", choices: [ { name: "Brouillon", @@ -233,26 +227,40 @@ export default { }, title: null, }, - paginatedFeatures: [], baseUrl: this.$store.state.configuration.BASE_URL, + modalAllDeleteOpen: false, map: null, zoom: null, lat: null, lng: null, featuresCount: 0, - next: null, - previous: null, + paginatedFeatures: [], pagination: { currentPage: 1, pagesize: 15, start: 0, end: 15, }, + previous: null, + next: null, + sort: { + column: "", + ascending: true, + }, showMap: true, showAddFeature: false, }; }, + watch: { + "form.type.selected"() { + this.fetchPagedFeatures(); + }, + "form.status.selected.value"() { + this.fetchPagedFeatures(); + }, + }, + computed: { ...mapGetters(["project", "permissions"]), ...mapState("feature", ["checkedFeatures"]), @@ -273,16 +281,6 @@ export default { featureTypeChoices() { return this.feature_types.map((el) => el.title); }, - - pageNumbers() { - const totalPages = Math.ceil( - this.featuresCount / this.pagination.pagesize - ); - return [...Array(totalPages).keys()].map((pageNumb) => { - ++pageNumb; - return pageNumb; - }); - }, }, methods: { @@ -349,7 +347,7 @@ export default { mapDefaultViewZoom, }); - this.getBbox2FIt(); + this.fetchBboxNfit(); document.addEventListener("change-layers-order", (event) => { // Reverse is done because the first layer in order has to be added in the map in last. @@ -372,7 +370,7 @@ export default { }, 1000); }, - getBbox2FIt(queryParams) { + fetchBboxNfit(queryParams) { featureAPI .getFeaturesBbox(this.project.slug, queryParams) .then((bbox) => { @@ -388,86 +386,42 @@ export default { return featureType ? featureType.slug : null; }, - buildFilterParams({ filterType, filterValue }) { - let params = ""; - let typeFilter, statusFilter; - //*** feature type ***// - if (filterType === "featureType") { - if (filterValue === "" && !this.form.type.selected) { - //* s'il y n'avait pas de filtre et qu'il a été supprimé --> ne pas mettre à jour les features - return "abort"; - } else if (filterValue !== undefined && filterValue !== null) { - //* s'il y a un nouveau filtre --> ajouter une params - typeFilter = this.getFeatureTypeSlug(filterValue); - } //* sinon il n'y a pas de param ajouté, ce qui supprime la query - - //*** status ***// - } else if (filterType === "status") { - if (filterValue === "" && !this.form.status.selected.value) { - return "abort"; - } else if (filterValue !== undefined && filterValue !== null) { - statusFilter = filterValue.value; - } - } - - //* after possibilities of aborting features fetch, empty geojson to make sure even no result would update - - if ( - (filterType === undefined || filterType === "status") && - this.form.type.selected - ) { - //* s'il y a déjà un filtre sélectionné, maintenir le params - typeFilter = this.getFeatureTypeSlug(this.form.type.selected); - } - if ( - (filterType === undefined || filterType === "featureType") && - this.form.status.selected.value - ) { - statusFilter = this.form.status.selected.value; - } - - if (typeFilter) { - let typeParams = `&feature_type_slug=${typeFilter}`; - params += typeParams; - } - if (statusFilter) { - let statusParams = `&status__value=${statusFilter}`; - params += statusParams; - } - - //*** title ***// - if (this.form.title) { - params += `&title=${this.form.title}`; + getAvalaibleField(orderField) { + let result = orderField; + if (orderField === "display_creator") { + result = "creator"; + } else if (orderField === "display_last_editor") { + result = "last_editor"; } - this.getBbox2FIt(params); - return params; - }, - - updateTypeFeatures(filterValue) { - //* only update:selection custom event can trigger the filter update, - //* but it happens before the value is updated, thus using selected value from event to update query - this.fetchPagedFeatures({ filterType: "featureType", filterValue }); + return result; }, - updateStatusFeatures(filterValue) { - this.fetchPagedFeatures({ filterType: "status", filterValue }); + buildQueryString() { + let urlParams = ""; + let typeFilter = this.getFeatureTypeSlug(this.form.type.selected); + let statusFilter = this.form.status.selected.value; + + if (typeFilter) urlParams += `&feature_type_slug=${typeFilter}`; + if (statusFilter) urlParams += `&status__value=${statusFilter}`; + if (this.form.title) urlParams += `&title=${this.form.title}`; + if (this.sort.column) { + urlParams += `&ordering=${ + this.sort.ascending ? "-" : "" + }${this.getAvalaibleField(this.sort.column)}`; + } + return urlParams; }, - fetchPagedFeatures(params) { - this.onFilterChange(); //* temporary, use paginated event to watch change in filters, to modify geojson on map + fetchPagedFeatures(newUrl) { + this.onFilterChange(); //* use paginated event to watch change in filters and modify features 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) { - if (typeof params === "object") { - const filterParams = this.buildFilterParams(params); - if (filterParams === "abort") return; - url += filterParams; - } else { - //console.error("ONLY FOR DEV !!!!!!!!!!!!!"); - //params = params.replace("8000", "8010"); //* for dev uncomment to use proxy link - url = params; - } + if (newUrl && typeof newUrl === "string") { + //* if receiving next & previous url + //newUrl = newUrl.replace("8000", "8010"); //* for dev uncomment to use proxy link + url = newUrl; } + const queryString = this.buildQueryString(); + url += queryString; this.$store.commit( "DISPLAY_LOADER", @@ -480,6 +434,8 @@ export default { this.next = data.next; this.paginatedFeatures = data.results.features; } + //* bbox needs to be updated with the same filters + if (queryString) this.fetchBboxNfit(queryString); this.$store.commit("DISCARD_LOADER"); }); }, @@ -497,6 +453,14 @@ export default { } }, + handleSortChange(sort) { + this.sort = sort; + this.fetchPagedFeatures({ + filterType: undefined, + filterValue: undefined, + }); + }, + toPage(pageNumber) { const toAddOrRemove = (pageNumber - this.pagination.currentPage) * this.pagination.pagesize;