diff --git a/package.json b/package.json
index 68149375b3688a6c70d571be6a49810c108baf32..9c54f9e0edfd6ca848a1c84a8bcde9746b2e0b28 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "geocontrib-frontend",
-  "version": "2.3.0",
+  "version": "2.3.1",
   "private": true,
   "scripts": {
     "serve": "npm run init-proxy & npm run init-serve",
diff --git a/src/App.vue b/src/App.vue
index b356b5c01613825879292d3e248f95963068fff8..349930d3af73b77c5b6e5aea03345776cb02a346 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -151,6 +151,7 @@
     </header>
     <main>
       <div id="content" class="ui stackable grid centered container">
+        <transition name="fadeDownUp">
         <div v-if="messages && messages.length > 0" class="row">
           <div class="fourteen wide column">
             <div
@@ -169,6 +170,7 @@
             </div>
           </div>
         </div>
+        </transition>
         <div :class="{ active: loader.isLoading }" class="ui inverted dimmer">
           <div class="ui text loader">
             {{ loader.message }}
@@ -373,5 +375,54 @@ footer {
   box-shadow: none !important;
   transition: none !important;
 }
+
+
+.bounce-enter-active {
+  animation: bounce-in .5s;
+}
+.bounce-leave-active {
+  animation: bounce-in .5s reverse;
+}
+@keyframes bounce-in {
+  0% {
+    transform: scale(0);
+  }
+  50% {
+    transform: scale(1.5);
+  }
+  100% {
+    transform: scale(1);
+  }
+}
+
+.fadeDownUp-enter-active {
+  animation: fadeInDown .5s;
+}
+.fadeDownUp-leave-active {
+  animation: fadeOutUp .5s;
+}
+@keyframes fadeOutUp {
+  0% {
+    opacity: 1;
+  }
+
+  100% {
+    opacity: 0;
+    transform: translate3d(0, -100%, 0);
+  }
+}
+
+@keyframes fadeInDown {
+  from {
+    opacity: 0;
+    transform: translate3d(0, -100%, 0);
+  }
+
+  to {
+    opacity: 1;
+    transform: translate3d(0, 0, 0);
+  }
+}
+
 </style>
  
\ No newline at end of file
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_detail.vue b/src/views/feature/Feature_detail.vue
index 885f0b701d38dd64b0cb348d93a9270f83e48915..f90fca2f9c7f9cbd1a5bd496cc69e068f2011fd0 100644
--- a/src/views/feature/Feature_detail.vue
+++ b/src/views/feature/Feature_detail.vue
@@ -238,7 +238,7 @@
           </div>
 
           <div
-            v-if="permissions && permissions.can_create_feature"
+            v-if="permissions && permissions.can_create_feature && isOffline() !== true"
             class="ui segment"
           >
             <form
@@ -249,7 +249,11 @@
                 <label :for="comment_form.comment.id_for_label"
                   >Ajouter un commentaire</label
                 >
-                {{ comment_form.comment.errors }}
+                <ul v-if="comment_form.comment.errors" class="errorlist">
+                  <li>
+                    {{ comment_form.comment.errors }}
+                  </li>
+                </ul>
                 <textarea
                   v-model="comment_form.comment.value"
                   :name="comment_form.comment.html_name"
@@ -292,7 +296,6 @@
                 </li>
               </ul>
               <button
-                v-if="isOffline() !== true"
                 @click="postComment"
                 type="button"
                 class="ui compact green icon button"
@@ -371,7 +374,7 @@ export default {
         comment: {
           id_for_label: "add-comment",
           html_name: "add-comment",
-          errors: null,
+          errors: "",
           value: null,
         },
       },
@@ -453,8 +456,18 @@ export default {
       this.addFeatureToMap();
     },
 
+    validateForm() {
+      this.comment_form.comment.errors = ""
+      if (!this.comment_form.comment.value) {
+        this.comment_form.comment.errors = "Le commentaire ne peut pas être vide"
+        return false;
+      }
+      return true;
+    },
+
     postComment() {
-      featureAPI
+      if (this.validateForm()) {
+        featureAPI
         .postComment({
           featureId: this.$route.params.slug_signal,
           comment: this.comment_form.comment.value,
@@ -476,6 +489,7 @@ export default {
             this.confirmComment();
           }
         });
+      }
     },
 
     confirmComment() {
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;
diff --git a/src/views/registration/Login.vue b/src/views/registration/Login.vue
index 0f62078398f1fe07998e86e91d73c4c2601cc8cf..1e3c5796a275ef52ad8932114c45c65373b7293e 100644
--- a/src/views/registration/Login.vue
+++ b/src/views/registration/Login.vue
@@ -2,10 +2,7 @@
   <div>
     <div class="row">
       <div class="fourteen wide column">
-        <img
-          class="ui centered small image"
-          :src="logo"
-        />
+        <img class="ui centered small image" :src="logo" />
         <h2 class="ui center aligned icon header">
           <div class="content">
             {{ APPLICATION_NAME }}
@@ -60,7 +57,6 @@
 </template>
 
 <script>
-
 export default {
   name: "Login",
   data() {
@@ -101,5 +97,15 @@ export default {
         .catch();
     },
   },
+
+  mounted() {
+    if (this.$store.state.user) {
+      this.$store.commit(
+        "DISPLAY_MESSAGE",
+        "Vous êtes déjà connecté, vous allez être rediriger vers la page d'accueil."
+      );
+      setTimeout(() => this.$router.push("/"), 3100);
+    }
+  },
 };
 </script>
\ No newline at end of file