diff --git a/src/App.vue b/src/App.vue
index 343ccfe54930b931fc6bc1111399d9ee6d749a6a..940e4ea9c7ee80e27d188c66d6a644d4be5a1ef1 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -151,6 +151,11 @@
             </div>
           </div>
         </div>
+        <div :class="{ active: loader.isLoading }" class="ui inverted dimmer">
+          <div class="ui text loader">
+            {{ loader.message }}
+          </div>
+        </div>
         <router-view />
         <!-- //* Les views sont injectées ici -->
       </div>
@@ -194,6 +199,7 @@ export default {
       "USER_LEVEL_PROJECTS",
       "configuration",
       "messages",
+      "loader",
     ]),
     ...mapGetters(["project"]),
     APPLICATION_NAME: function () {
@@ -256,6 +262,11 @@ body {
   flex-direction: column;
 }
 
+/* to display loader between header and footer */
+main {
+  position: relative;
+}
+
 footer {
   margin-top: auto;
 }
@@ -269,28 +280,32 @@ footer {
   background: white !important;
 }
 
+.flex {
+  display: flex;
+}
+
+/* keep above loader */
+#menu-dropdown {
+  z-index: 1001;
+}
+
 @media screen and (min-width: 560px) {
   .mobile {
     display: none !important;
   }
-
   .header-menu {
     min-width: 560px;
   }
-
   .menu.container {
     width: auto !important;
     margin-left: 1em !important;
     margin-right: 1em !important;
   }
-
   .push-right-desktop {
     margin-left: auto;
   }
 }
-.flex {
-  display: flex;
-}
+
 @media screen and (max-width: 560px) {
   .desktop {
     display: none !important;
diff --git a/src/components/feature/FeatureAttachmentForm.vue b/src/components/feature/FeatureAttachmentForm.vue
index 83343c7935dab9d69dee36ccbde5b2315927da43..eb7f1aa25b5de25e34e80ea718e26d63373efe01 100644
--- a/src/components/feature/FeatureAttachmentForm.vue
+++ b/src/components/feature/FeatureAttachmentForm.vue
@@ -23,7 +23,6 @@
               :name="form.title.html_name"
               :id="form.title.id_for_label"
               v-model="form.title.value"
-              
             />
             <ul :id="form.title.id_for_error" class="errorlist">
               <li v-for="error in form.title.errors" :key="error">
@@ -46,6 +45,7 @@
             <input
               @change="onFileChange"
               type="file"
+              accept="application/pdf, image/jpeg, image/png"
               style="display: none"
               :name="form.attachment_file.html_name"
               :id="'attachment_file' + attachmentForm.dataKey"
@@ -118,21 +118,23 @@ export default {
     attachmentForm(newValue) {
       this.initForm(newValue);
     },
+    //* utilisation de watcher, car @change aurait un délai
     "form.title.value": function (newValue, oldValue) {
-      if (oldValue != ''){
-        if (newValue != oldValue){
+      if (oldValue != "") {
+        if (newValue != oldValue) {
           this.updateStore();
         }
       }
     },
     "form.info.value": function (newValue, oldValue) {
-       if (oldValue != ''){
-        if (newValue != oldValue){
+      if (oldValue != "") {
+        if (newValue != oldValue) {
           this.updateStore();
         }
       }
     },
   },
+
   methods: {
     initForm(attachmentForm) {
       for (let el in attachmentForm) {
@@ -152,10 +154,7 @@ export default {
         this.attachmentForm.dataKey
       );
       if (this.form.id.value)
-        this.$store.commit(
-          "feature/DELETE_ATTACHMENTS",
-          this.form.id.value
-        );
+        this.$store.commit("feature/DELETE_ATTACHMENTS", this.form.id.value);
     },
 
     updateStore() {
@@ -166,22 +165,54 @@ export default {
         attachment_file: this.form.attachment_file.value,
         info: this.form.info.value,
         fileToImport: this.fileToImport,
-      }
+      };
       this.$store.commit("feature/UPDATE_ATTACHMENT_FORM", data);
-      if (data.id){
-        this.$store.commit(
-          "feature/PUT_ATTACHMENTS",
-          data
-        );
+      if (data.id) {
+        this.$store.commit("feature/PUT_ATTACHMENTS", data);
       }
     },
 
+    validateImgFile(files, handleFile) {
+      let url = window.URL || window.webkitURL;
+      let image = new Image();
+      image.onload = function () {
+        handleFile(true);
+      };
+      image.onerror = function () {
+        handleFile(false);
+      };
+      image.src = url.createObjectURL(files);
+      URL.revokeObjectURL(image.src);
+    },
+
     onFileChange(e) {
+      // * read image file
       const files = e.target.files || e.dataTransfer.files;
-      if (!files.length) return;
-      this.fileToImport = files[0]; //* store file to import
-      this.form.attachment_file.value = files[0].name; //* add name to the form for display, in order to match format return from API
-      this.updateStore();
+
+      const _this = this; //* 'this' is different in onload function
+      function handleFile(isValid) {
+        if (isValid) {
+          _this.fileToImport = files[0]; //* store the file to post later
+          _this.form.attachment_file.value = files[0].name; //* add name to the form for display, in order to match format return from API
+          _this.updateStore();
+          _this.form.attachment_file.errors = [];
+        } else {
+          _this.form.attachment_file.errors.push(
+            "Transférez une image valide. Le fichier que vous avez transféré n'est pas une image, ou il est corrompu."
+          );
+        }
+      }
+
+      if (files.length) {
+        //* exception for pdf
+        if (files[0].type === "application/pdf") {
+          handleFile(true);
+        } else {
+          this.form.attachment_file.errors = [];
+          //* check if file is an image and pass callback to handle file
+          this.validateImgFile(files[0], handleFile);
+        }
+      }
     },
 
     checkForm() {
diff --git a/src/components/feature/FeatureListTable.vue b/src/components/feature/FeatureListTable.vue
index ed7f0dac060041a1badf0b1f4137cf458d8ba7b6..c7ebb5c23e78db573e2df94005f12a000d1f2a3b 100644
--- a/src/components/feature/FeatureListTable.vue
+++ b/src/components/feature/FeatureListTable.vue
@@ -71,8 +71,7 @@
                 type="checkbox"
                 :id="feature.id"
                 :value="feature.id"
-                v-model="checkedFeatures"
-                :checked="checkedFeatures[feature.id]"
+                v-model="checked"
               />
               <label></label>
             </div>
@@ -280,6 +279,15 @@ export default {
       return arr;
     },
 
+    checked: {
+      get() {
+        return this.checkedFeatures;
+      },
+      set(newChecked) {
+        this.$store.commit("feature/UPDATE_CHECKED_FEATURES", newChecked);
+      },
+    },
+
     displayedPageEnd() {
       return this.filteredFeatures.length <= this.pagination.end
         ? this.filteredFeatures.length
diff --git a/src/components/feature_type/FeatureTypeCustomForm.vue b/src/components/feature_type/FeatureTypeCustomForm.vue
index 39d1349eacbe108c3e4679165da14a850583b338..5372073ee270dca4dba201cfdf927b089512c8f8 100644
--- a/src/components/feature_type/FeatureTypeCustomForm.vue
+++ b/src/components/feature_type/FeatureTypeCustomForm.vue
@@ -106,7 +106,7 @@
             v-model="arrayOption"
             class="options-field"
           />
-          <small>{{ form.help_text }}</small>
+          <small>{{ form.options.help_text }}</small>
           <ul id="errorlist" class="errorlist">
             <li v-for="error in form.options.errors" :key="error">
               {{ error }}
@@ -195,7 +195,7 @@ export default {
           id_for_label: "options",
           label: "Options",
           html_name: "options",
-          help_text: "",
+          help_text: "Valeurs possibles de ce champ, séparées par des virgules",
           field: {
             max_length: 256,
           },
@@ -284,46 +284,37 @@ export default {
     },
 
     checkUniqueName() {
-      console.log(this.$store);
-      console.log(this.$store.state);
-      console.log(this.$store.state.feature_type);
-      if (this.form.name.value) {
-        const occurences = this.$store.state.feature_type.customForms
-          .map((el) => el.name)
-          .filter((el) => el === this.form.name.value);
-        console.log("occurences", occurences);
-        console.log(occurences.length);
-        if (occurences.length > 1) {
-          console.log("duplicate", this.form.name.value);
-          this.form.name.errors = [
-            "Les champs personnalisés ne peuvent pas avoir des noms similaires.",
-          ];
-          return false;
-        }
-      }
-      this.form.name.errors = [];
-      return true;
+      const occurences = this.$store.state.feature_type.customForms
+        .map((el) => el.name)
+        .filter((el) => el === this.form.name.value);
+      return occurences.length === 1;
     },
 
     checkCustomForm() {
-      if (this.form.label.value === null) {
+      this.form.label.errors = [];
+      this.form.name.errors = [];
+      if (!this.form.label.value) {
+        //* vérifier que le label est renseigné
         this.form.label.errors = ["Veuillez compléter ce champ."];
         return false;
-      } else if (this.form.name.value === null) {
+      } else if (!this.form.name.value) {
+        //* vérifier que le nom est renseigné
         this.form.name.errors = ["Veuillez compléter ce champ."];
-        this.form.label.errors = [];
         return false;
       } else if (!this.hasRegularCharacters(this.form.name.value)) {
+        //* vérifier qu'il n'y a pas de caractères spéciaux
         this.form.name.errors = [
           "Veuillez utiliser seulement les caratères autorisés.",
         ];
-        this.form.label.errors = [];
         return false;
-      } else if (this.checkUniqueName()) {
-        this.form.label.errors = [];
-        this.form.name.errors = [];
-        return true;
+      } else if (!this.checkUniqueName()) {
+        //* vérifier si les noms sont pas dupliqués
+        this.form.name.errors = [
+          "Les champs personnalisés ne peuvent pas avoir des noms similaires.",
+        ];
+        return false;
       }
+      return true;
     },
   },
 
diff --git a/src/store/index.js b/src/store/index.js
index eef57b2b52cbdccab9fc2c740888b4b501b446f8..6f543f6d8a48b83ba0b027c7cf7c051f5c4a6a75 100644
--- a/src/store/index.js
+++ b/src/store/index.js
@@ -48,12 +48,11 @@ export default new Vuex.Store({
     USER_LEVEL_PROJECTS: null,
     user_permissions: null,
     messages: [],
-    events: null
-    // events: {
-    //   'events': null,
-    //   'features': null,
-    //   'comments': null
-    // }
+    events: null,
+    loader: {
+      isLoading: false,
+      message: "En cours de chargement"
+    },
   },
 
   mutations: {
@@ -101,14 +100,23 @@ export default new Vuex.Store({
     },
     DISPLAY_MESSAGE(state, comment) {
       state.messages = [{ comment }, ...state.messages];
-      document.getElementById("messages").scrollIntoView({ block: "start", inline: "nearest" });
+      if (document.getElementById("content")) document.getElementById("content").scrollIntoView({ block: "start", inline: "nearest" });
       setTimeout(() => {
         state.messages = [];
       }, 3000);
     },
     CLEAR_MESSAGES(state) {
       state.messages = [];
-    }
+    },
+    DISPLAY_LOADER(state, message) {
+      state.loader = { isLoading: true, message }
+    },
+    DISCARD_LOADER(state) {
+      state.loader = {
+        isLoading: false,
+        message: "En cours de chargement"
+      };
+    },
   },
 
   getters: {
diff --git a/src/store/modules/feature.js b/src/store/modules/feature.js
index 3e248934f355806b289669ced8beaf24348457d8..9b629c72ca3f301918d20e238778fe29739d02e9 100644
--- a/src/store/modules/feature.js
+++ b/src/store/modules/feature.js
@@ -14,10 +14,11 @@ const feature = {
     attachmentFormset: [],
     attachmentsToDelete: [],
     attachmentsToPut: [],
-    linkedFormset: [],
+    checkedFeatures: [],
+    extra_form: [],
     features: [],
     form: null,
-    extra_form: [],
+    linkedFormset: [],
     linked_features: [],
     statusChoices: [
       {
@@ -97,6 +98,9 @@ const feature = {
     REMOVE_ATTACHMENTS_ID_TO_DELETE(state, attachementId) {
       state.attachmentsToDelete = state.attachmentsToDelete.filter(el => el !== attachementId);
     },
+    UPDATE_CHECKED_FEATURES(state, checkedFeatures) {
+      state.checkedFeatures = checkedFeatures;
+    }
   },
   getters: {
   },
@@ -117,9 +121,12 @@ const feature = {
         });
     },
 
-    SEND_FEATURE({ state, rootState, dispatch }, routeName) {
+    SEND_FEATURE({ state, rootState, commit, dispatch }, routeName) {
+      commit("DISPLAY_LOADER", "Le signalement est en cours de création", { root: true })
       const message = routeName === "editer-signalement" ? "Le signalement a été mis à jour" : "Le signalement a été crée";
+
       function redirect(featureId) {
+        commit("DISCARD_LOADER", null, { root: true })
         router.push({
           name: "details-signalement",
           params: {
@@ -129,12 +136,14 @@ const feature = {
           },
         });
       }
+
       async function handleOtherForms(featureId) {
         await dispatch("SEND_ATTACHMENTS", featureId)
         await dispatch("PUT_LINKED_FEATURES", featureId)
         redirect(featureId);
       }
 
+      //* prepare feature data to send
       let extraFormObject = {}; //* prepare an object to be flatten in properties of geojson
       for (const field of state.extra_form) {
         extraFormObject[field.name] = field.value;
@@ -152,8 +161,9 @@ const feature = {
           ...extraFormObject
         }
       }
+
       if (routeName === "editer-signalement") {
-        axios
+        return axios
           .put(`${rootState.configuration.VUE_APP_DJANGO_API_BASE}features/${state.form.feature_id}/?` +
             `feature_type__slug=${rootState.feature_type.current_feature_type_slug}` +
             `&project__slug=${rootState.project_slug}`
@@ -168,10 +178,11 @@ const feature = {
             }
           })
           .catch((error) => {
+            commit("DISCARD_LOADER", null, { root: true })
             throw error;
           });
       } else {
-        axios
+        return axios
           .post(`${rootState.configuration.VUE_APP_DJANGO_API_BASE}features/`, geojson)
           .then((response) => {
             if (response.status === 201 && response.data) {
@@ -183,6 +194,7 @@ const feature = {
             }
           })
           .catch((error) => {
+            commit("DISCARD_LOADER", null, { root: true })
             throw error;
           });
       }
diff --git a/src/views/feature/Feature_detail.vue b/src/views/feature/Feature_detail.vue
index 2a2951a8d3af652b8ac345312e54333aca278b37..5d929e5b58ada4df68fcd81071f589d9cb4ac30b 100644
--- a/src/views/feature/Feature_detail.vue
+++ b/src/views/feature/Feature_detail.vue
@@ -37,8 +37,9 @@
                 >
                   <i class="inverted grey pencil alternate icon"></i>
                 </router-link>
+                <!-- (permissions && permissions.can_delete_feature) || -->
                 <a
-                  v-if="permissions && permissions.can_delete_feature"
+                  v-if="isFeatureCreator"
                   @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 c948a71abe0305befdb0530801dd065422f99a9d..e18201375c735d01e37870afb4c5c2d8aa705f09 100644
--- a/src/views/feature/Feature_edit.vue
+++ b/src/views/feature/Feature_edit.vue
@@ -74,7 +74,7 @@
           <div v-frag v-if="feature_type && feature_type.geom_type === 'point'">
             <p>
               <button
-                @click="showGeoRef = true"
+                @click="toggleGeoRefModal"
                 id="add-geo-image"
                 type="button"
                 class="ui compact button"
@@ -84,44 +84,69 @@
               Vous pouvez utiliser une image géoréférencée pour localiser le
               signalement.
             </p>
-            <div v-if="showGeoRef">
-              <p>
-                Attention, si vous avez déjà saisi une géométrie, celle issue de
-                l'image importée l'écrasera.
-              </p>
-              <div class="field georef-btn">
-                <label>Image (png ou jpeg)</label>
-                <label class="ui icon button" for="image_file">
-                  <i class="file icon"></i>
-                  <span class="label">{{ geoRefFileLabel }}</span>
-                </label>
-                <input
-                  type="file"
-                  accept="image/jpeg, image/png"
-                  style="display: none"
-                  ref="file"
-                  v-on:change="handleFileUpload()"
-                  name="image_file"
-                  class="image_file"
-                  id="image_file"
-                />
-                <p class="error-message" style="color: red">
-                  {{ erreurUploadMessage }}
-                </p>
-              </div>
-              <button
-                @click="georeferencement()"
-                id="get-geom-from-image-file"
-                type="button"
-                class="ui positive right labeled icon button"
+
+            <div
+              v-if="showGeoRef"
+              class="ui dimmer modals page transition visible active"
+              style="display: flex !important"
+            >
+              <div
+                class="ui mini modal transition visible active"
+                style="display: block !important"
               >
-                Importer
-                <i class="checkmark icon"></i>
-              </button>
+                <i class="close icon" @click="toggleGeoRefModal"></i>
+                <div class="content">
+                  <h3>Importer une image géoréférencée</h3>
+                  <form
+                    id="form-geo-image"
+                    class="ui form"
+                    enctype="multipart/form-data"
+                  >
+                    <p>
+                      Attention, si vous avez déjà saisi une géométrie, celle
+                      issue de l'image importée l'écrasera.
+                    </p>
+                    <div class="field georef-btn">
+                      <label>Image (png ou jpeg)</label>
+                      <label class="ui icon button" for="image_file">
+                        <i class="file icon"></i>
+                        <span class="label">{{ geoRefFileLabel }}</span>
+                      </label>
+                      <input
+                        type="file"
+                        accept="image/jpeg, image/png"
+                        style="display: none"
+                        ref="file"
+                        v-on:change="handleFileUpload"
+                        name="image_file"
+                        class="image_file"
+                        id="image_file"
+                      />
+                      <p class="error-message" style="color: red">
+                        {{ erreurUploadMessage }}
+                      </p>
+                    </div>
+                    <button
+                      @click="georeferencement"
+                      id="get-geom-from-image-file"
+                      type="button"
+                      :class="[
+                        'ui compact button',
+                        file && !erreurUploadMessage ? 'green' : 'disabled',
+                        { red: erreurUploadMessage },
+                      ]"
+                    >
+                      <i class="plus icon"></i>
+                      Importer
+                    </button>
+                  </form>
+                </div>
+              </div>
             </div>
+
             <p v-if="showGeoPositionBtn">
               <button
-                @click="create_point_geoposition()"
+                @click="create_point_geoposition"
                 id="create-point-geoposition"
                 type="button"
                 class="ui compact button"
@@ -173,7 +198,7 @@
         <!-- Extra Fields -->
         <div class="ui horizontal divider">DONNÉES MÉTIER</div>
         <div
-          v-for="(field, index) in extra_form"
+          v-for="(field, index) in orderedCustomFields"
           :key="field.field_type + index"
           class="field"
         >
@@ -350,6 +375,12 @@ export default {
       );
     },
 
+    orderedCustomFields() {
+      return [...this.extra_form].sort(
+        (a, b) => a.position - b.position
+      );
+    },
+
     geoRefFileLabel() {
       if (this.file) {
         return this.file.name;
@@ -441,6 +472,10 @@ export default {
 
       function error(err) {
         this.erreurGeolocalisationMessage = err.message;
+        if (err.message === "User denied geolocation prompt") {
+          this.erreurGeolocalisationMessage =
+            "La géolocalisation a été désactivé par l'utilisateur";
+        }
       }
       this.erreurGeolocalisationMessage = null;
       if (!navigator.geolocation) {
@@ -454,7 +489,17 @@ export default {
       }
     },
 
+    toggleGeoRefModal() {
+      if (this.showGeoRef) {
+        //* when popup closes, empty form
+        this.erreurUploadMessage = "";
+        this.file = null;
+      }
+      this.showGeoRef = !this.showGeoRef;
+    },
+
     handleFileUpload() {
+      this.erreurUploadMessage = "";
       this.file = this.$refs.file.files[0];
     },
 
@@ -464,19 +509,17 @@ export default {
       let formData = new FormData();
       formData.append("image_file", this.file);
       console.log(">> formData >> ", formData);
-      let self = this;
       axios
         .post(url, formData, {
           headers: {
             "Content-Type": "multipart/form-data",
           },
         })
-        .then(function (response) {
+        .then((response) => {
           console.log("SUCCESS!!", response.data);
           if (response.data.geom.indexOf("POINT") >= 0) {
             let regexp = /POINT\s\((.*)\s(.*)\)/;
-            var arr = regexp.exec(response.data.geom);
-
+            let arr = regexp.exec(response.data.geom);
             let json = {
               type: "Feature",
               geometry: {
@@ -485,21 +528,21 @@ export default {
               },
               properties: {},
             };
-            self.updateMap(json);
-            self.updateGeomField(json);
+            this.updateMap(json);
+            this.updateGeomField(json);
             // Set Attachment
-            self.addAttachment({
+            this.addAttachment({
               title: "Localisation",
               info: "",
               id: "loc",
-              attachment_file: self.file.name,
-              fileToImport: self.file,
+              attachment_file: this.file.name,
+              fileToImport: this.file,
             });
           }
         })
-        .catch(function (response) {
+        .catch((response) => {
           console.log("FAILURE!!");
-          self.erreurUploadMessage = response.data.message;
+          this.erreurUploadMessage = response.data.message;
         });
     },
 
@@ -939,7 +982,7 @@ export default {
         this.initForm();
         this.initMap();
         this.onFeatureTypeLoaded();
-        this.initExtraForms();
+        this.initExtraForms(this.feature);
 
         setTimeout(
           function () {
@@ -985,4 +1028,9 @@ export default {
 .ui.segment {
   margin: 1rem 0 !important;
 }
+/* override to display buttons under the dimmer of modal */
+.leaflet-top,
+.leaflet-bottom {
+  z-index: 800;
+}
 </style>
diff --git a/src/views/feature/Feature_list.vue b/src/views/feature/Feature_list.vue
index 79b7f1342d4563e29850e5e7793f52d0eaa0c0b7..e3906d9f6020911433cbd6fcf232f1cd49506ce5 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 class="ui dimmer" :class="[{ active: featureLoading }]"></div>
     <script
       type="application/javascript"
       :src="
@@ -38,7 +37,11 @@
           </div>
 
           <div
-            v-if="project && feature_types && permissions.can_create_feature"
+            v-if="
+              project &&
+              feature_types.length > 0 &&
+              permissions.can_create_feature
+            "
             class="item right"
           >
             <div
@@ -88,7 +91,6 @@
       <div class="field wide four column no-margin-mobile">
         <label>Type</label>
         <Dropdown
-          @update:selection="onFilterTypeChange($event)"
           :options="form.type.choices"
           :selected="form.type.selected"
           :selection.sync="form.type.selected"
@@ -100,8 +102,7 @@
         <label>Statut</label>
         <!--  //* giving an object mapped on key name -->
         <Dropdown
-          @update:selection="onFilterStatusChange($event)"
-          :options="form.status.choices"
+          :options="statusChoices"
           :selected="form.status.selected.name"
           :selection.sync="form.status.selected"
           :search="true"
@@ -117,7 +118,7 @@
               type="text"
               name="title"
               v-model="form.title"
-              @input="onFilterChange()"
+              @input="onFilterChange"
             />
             <button
               type="button"
@@ -129,7 +130,7 @@
           </div>
         </div>
       </div>
-      <!-- map params, updated on map move // todo : brancher sur la carte probablement -->
+      <!-- map params, updated on map move -->
       <input type="hidden" name="zoom" v-model="zoom" />
       <input type="hidden" name="lat" v-model="lat" />
       <input type="hidden" name="lng" v-model="lng" />
@@ -199,7 +200,6 @@ export default {
   data() {
     return {
       modalAllDeleteOpen: false,
-      checkedFeatures: [],
       form: {
         type: {
           selected: null,
@@ -233,7 +233,6 @@ export default {
       },
 
       geojsonFeatures: [],
-      featureLoading: false,
       filterStatus: null,
       filterType: null,
       baseUrl: this.$store.state.configuration.BASE_URL,
@@ -249,7 +248,7 @@ export default {
   computed: {
     ...mapGetters(["project", "permissions"]),
     ...mapState(["user"]),
-    ...mapState("feature", ["features"]),
+    ...mapState("feature", ["features", "checkedFeatures"]),
     ...mapState("feature_type", ["feature_types"]),
 
     baseMaps() {
@@ -258,15 +257,15 @@ export default {
 
     filteredFeatures() {
       let results = this.geojsonFeatures;
-      if (this.filterType) {
+      if (this.form.type.selected) {
         results = results.filter(
-          (el) => el.properties.feature_type.title === this.filterType
+          (el) => el.properties.feature_type.title === this.form.type.selected
         );
       }
-      if (this.filterStatus) {
-        console.log("filter by" + this.filterStatus);
+      if (this.form.status.selected.value) {
+        console.log("filter by" + this.form.status.selected.value);
         results = results.filter(
-          (el) => el.properties.status.value === this.filterStatus
+          (el) => el.properties.status.value === this.form.status.selected.value
         );
       }
       if (this.form.title) {
@@ -281,6 +280,21 @@ export default {
       }
       return results;
     },
+
+    statusChoices() {
+      //* if project is not moderate, remove pending status
+      return this.form.status.choices.filter((el) =>
+        this.project.moderation ? true : el.value !== "pending"
+      );
+    },
+  },
+
+  watch: {
+    filteredFeatures(newValue, oldValue) {
+      if (newValue && newValue !== oldValue) {
+        this.onFilterChange()
+      }
+    }
   },
 
   methods: {
@@ -311,30 +325,11 @@ export default {
       this.modalAllDelete();
     },
 
-    onFilterStatusChange(newvalue) {
-      this.filterStatus = null;
-      if (newvalue) {
-        console.log("filter change", newvalue.value);
-        this.filterStatus = newvalue.value;
-      }
-
-      this.onFilterChange();
-    },
-
-    onFilterTypeChange(newvalue) {
-      this.filterType = null;
-      if (newvalue) {
-        console.log("filter change", newvalue);
-        this.filterType = newvalue;
-      }
-      this.onFilterChange();
-    },
-
     onFilterChange() {
-      var features = this.filteredFeatures;
-      this.featureGroup.clearLayers();
-      this.featureGroup = mapUtil.addFeatures(features, {});
-      if (features.length > 0) {
+      if (this.featureGroup) {
+        const features = this.filteredFeatures;
+        this.featureGroup.clearLayers();
+        this.featureGroup = mapUtil.addFeatures(features, {});
         mapUtil
           .getMap()
           .fitBounds(this.featureGroup.getBounds(), { padding: [25, 25] });
@@ -365,19 +360,23 @@ export default {
       });
 
       // --------- End sidebar events ----------
-      if (this.$store.state.map.geojsonFeatures) {
-        this.loadFeatures(this.$store.state.map.geojsonFeatures);
-      } else {
+      if (this.features && this.features.length > 0) {
+        //* features are updated consistently, then if features exists, we can fetch the geojson version
         const url = `${this.$store.state.configuration.VUE_APP_DJANGO_API_BASE}projects/${this.$route.params.slug}/feature/?output=geojson`;
-        this.featureLoading = true;
+        this.$store.commit(
+          "DISPLAY_LOADER",
+          "Récupération des signalements en cours..."
+        );
         axios
           .get(url)
           .then((response) => {
-            this.loadFeatures(response.data.features);
-            this.featureLoading = false;
+            if (response.status === 200 && response.data.features.length > 0) {
+              this.loadFeatures(response.data.features);
+            }
+            this.$store.commit("DISCARD_LOADER");
           })
           .catch((error) => {
-            this.featureLoading = false;
+            this.$store.commit("DISCARD_LOADER");
             throw error;
           });
       }
@@ -429,6 +428,11 @@ export default {
   mounted() {
     this.initMap();
   },
+
+  destroyed() {
+    //* allow user to change page if ever stuck on loader
+    this.$store.commit("DISCARD_LOADER");
+  },
 };
 </script>
 
diff --git a/src/views/feature_type/Feature_type_detail.vue b/src/views/feature_type/Feature_type_detail.vue
index 37a732a06ff1fba6c92e8eeb21af894c752b39a8..e0dbf923db86d99d0daf3ae7b35f62c5396948d3 100644
--- a/src/views/feature_type/Feature_type_detail.vue
+++ b/src/views/feature_type/Feature_type_detail.vue
@@ -35,7 +35,7 @@
           <h3 class="ui header">Champs</h3>
           <div class="ui divided list">
             <div
-              v-for="(field, index) in structure.customfield_set"
+              v-for="(field, index) in orderedCustomFields"
               :key="field.name + index"
               class="item"
             >
@@ -154,10 +154,10 @@
             }}
           </div>
           <div>
-            Créé le {{ feature.created_on }}
-            <span v-if="$store.state.user.is_authenticated">
+            [ Créé le {{ feature.created_on | formatDate }}
+            <span v-if="$store.state.user">
               par {{ feature.display_creator }}</span
-            >
+            > ]
           </div>
         </div>
       </div>
@@ -204,6 +204,14 @@ export default {
     };
   },
 
+  filters: {
+    formatDate(value) {
+      let date = new Date(value);
+      date = date.toLocaleString().replace(",", " à");
+      return date.substr(0, date.length - 3); //* quick & dirty way to remove seconds from date
+    },
+  },
+
   computed: {
     ...mapGetters(["project", "permissions"]),
     ...mapState("feature", ["features"]),
@@ -226,6 +234,12 @@ export default {
     lastFeatures: function () {
       return this.feature_type_features.slice(0, 5);
     },
+
+    orderedCustomFields() {
+      return [...this.structure.customfield_set].sort(
+        (a, b) => a.position - b.position
+      );
+    },
   },
 
   methods: {
diff --git a/src/views/feature_type/Feature_type_edit.vue b/src/views/feature_type/Feature_type_edit.vue
index 691443a6d7d5ce34aa95be04ca40851e3e536a85..8786d6d25fd55758885d54420e66447efed1845d 100644
--- a/src/views/feature_type/Feature_type_edit.vue
+++ b/src/views/feature_type/Feature_type_edit.vue
@@ -153,7 +153,6 @@
           <i class="white save icon"></i>
           Créer et importer le(s) signalement(s) du geojson
         </button>
-
       </form>
     </div>
   </div>
@@ -345,13 +344,14 @@ export default {
     },
 
     checkCustomForms() {
+      let is_valid = true;
       if (this.$refs.customForms)
         for (const customForm of this.$refs.customForms) {
           if (customForm.checkCustomForm() === false) {
-            return false;
+            is_valid = false;
           }
         }
-      return true; //* fallback if all customForms returned true
+      return is_valid; //* fallback if all customForms returned true
     },
 
     checkForms() {
diff --git a/src/views/project/Project_detail.vue b/src/views/project/Project_detail.vue
index 5087954315d796ba8161c98966784ce467d44505..b77188127e9e778f1f8b306569f250629617dff8 100644
--- a/src/views/project/Project_detail.vue
+++ b/src/views/project/Project_detail.vue
@@ -590,7 +590,6 @@ export default {
     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;
       axios
         .get(url)
         .then((response) => {
@@ -600,7 +599,9 @@ export default {
             mapUtil
               .getMap()
               .fitBounds(featureGroup.getBounds(), { padding: [25, 25] });
-            self.$store.commit("map/SET_GEOJSON_FEATURES", features);
+            this.$store.commit("map/SET_GEOJSON_FEATURES", features);
+          } else {
+            this.$store.commit("map/SET_GEOJSON_FEATURES", []);
           }
         })
         .catch((error) => {
diff --git a/src/views/project/Project_members.vue b/src/views/project/Project_members.vue
index a838d755e80c0131efa9de35a3b5b1c63955eea9..dd1fc1c606342a8a6240da3d574f75852a5dca56 100644
--- a/src/views/project/Project_members.vue
+++ b/src/views/project/Project_members.vue
@@ -132,7 +132,12 @@ export default {
     },
 
     async populateMembers() {
+      this.$store.commit(
+        "DISPLAY_LOADER",
+        "Récupération des membres en cours..."
+      );
       await this.fetchMembers().then((members) => {
+        this.$store.commit("DISCARD_LOADER");
         this.projectMembers = members.map((el) => {
           return {
             userLevel: { name: el.level.display, value: el.level.codename },
@@ -149,5 +154,10 @@ export default {
     }
     this.populateMembers();
   },
+
+  destroyed() {
+    //* allow user to change page if ever stuck on loader
+    this.$store.commit("DISCARD_LOADER");
+  },
 };
 </script>
\ No newline at end of file