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 974ee922ebc6016d1a10d1841575d1ae13adb989..67079e83ac176f0bbc48a085eb788aef6630ef37 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: [],
   },
   mutations: {
@@ -79,6 +80,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: {
   },
@@ -137,9 +141,9 @@ const feature = {
       if (routeName === "editer-signalement") {
         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}` 
-          , geojson)
+            `feature_type__slug=${rootState.feature_type.current_feature_type_slug}` +
+            `&project__slug=${rootState.project_slug}`
+            , geojson)
           .then((response) => {
             if (response.status === 200 && response.data) {
               if (state.attachmentFormset.length > 0 || state.linkedFormset.length > 0) {
@@ -189,7 +193,7 @@ const feature = {
               console.error(error);
               return error
             });
-            
+
         }
       }
 
@@ -203,7 +207,7 @@ const feature = {
         if (attachment.title)
           data['info'] = attachment.info
         formdataToUpdate.append("data", JSON.stringify(data));
-        let payload ={
+        let payload = {
           'attachmentsId': attachment.id,
           'featureId': featureId,
           'formdataToUpdate': formdataToUpdate
@@ -213,7 +217,7 @@ const feature = {
       }
 
       function deleteAttachement(attachmentsId, featureId) {
-        let payload ={
+        let payload = {
           'attachmentsId': attachmentsId,
           'featureId': featureId
         }
@@ -224,7 +228,7 @@ const feature = {
         ...state.attachmentFormset.map((attachment) => postAttachement(attachment)),
         ...state.attachmentsToPut.map((attachments) => putAttachement(attachments, featureId)),
         ...state.attachmentsToDelete.map((attachmentsId) => deleteAttachement(attachmentsId, featureId))
-        ]
+      ]
       );
       state.attachmentsToDelete = []
       state.attachmentsToPut = []
@@ -280,8 +284,8 @@ const feature = {
     DELETE_FEATURE({ state, rootState }, feature_id) {
       console.log("Deleting feature:", feature_id, state)
       const url = `${rootState.configuration.VUE_APP_DJANGO_API_BASE}features/${feature_id}/?` +
-      `feature_type__slug=${rootState.feature_type.current_feature_type_slug}` + 
-      `&project__slug=${rootState.project_slug}`;
+        `feature_type__slug=${rootState.feature_type.current_feature_type_slug}` +
+        `&project__slug=${rootState.project_slug}`;
       return axios
         .delete(url)
         .then((response) => response)
diff --git a/src/views/feature/Feature_detail.vue b/src/views/feature/Feature_detail.vue
index 62203ff90acf0230451cf02f0f3e22c85579c717..42a6d487f1f1eb874b01ae4d62d4f0a0d451bc98 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"
@@ -101,7 +102,7 @@
                     v-if="feature.status"
                     :class="getIconLabelStatus(feature.status, 'icon')"
                   ></i>
-                  {{ getIconLabelStatus(feature.status, 'label') }}
+                  {{ getIconLabelStatus(feature.status, "label") }}
                 </td>
               </tr>
               <tr>
@@ -417,7 +418,6 @@ export default {
       const result = this.$store.state.feature.features.find(
         (el) => el.feature_id === this.$route.params.slug_signal
       );
-      console.log("result", result);
       return result;
     },
 
@@ -438,23 +438,19 @@ export default {
   },
 
   methods: {
-    getIconLabelStatus(status, type){
-      if (status  === 'archived')
-        if (type == 'icon')
-          return "grey archive icon";
-        else return 'Archivé';
-      else if (status === 'pending')
-        if (type == 'icon')
-          return "teal hourglass outline icon";
-        else return 'En attente de publication';
-      else if (status === 'published')
-        if (type == 'icon')
-          return "olive check icon";
-        else return 'Publié';
-      else if (status === 'draft')
-        if (type == 'icon')
-          return "orange pencil alternate icon";
-        else return 'Brouillon';
+    getIconLabelStatus(status, type) {
+      if (status === "archived")
+        if (type == "icon") return "grey archive icon";
+        else return "Archivé";
+      else if (status === "pending")
+        if (type == "icon") return "teal hourglass outline icon";
+        else return "En attente de publication";
+      else if (status === "published")
+        if (type == "icon") return "olive check icon";
+        else return "Publié";
+      else if (status === "draft")
+        if (type == "icon") return "orange pencil alternate icon";
+        else return "Brouillon";
     },
     pushNgo(link) {
       this.$router.push({
@@ -533,7 +529,10 @@ export default {
         .dispatch("feature/DELETE_FEATURE", this.feature.feature_id)
         .then((response) => {
           if (response.status === 204) {
-            this.$store.dispatch("feature/GET_PROJECT_FEATURES", this.$route.params.slug);
+            this.$store.dispatch(
+              "feature/GET_PROJECT_FEATURES",
+              this.$route.params.slug
+            );
             this.goBackToProject();
           }
         });
@@ -599,7 +598,9 @@ export default {
           if (feature) {
             const currentFeature = [feature];
             const featureGroup = mapUtil.addFeatures(currentFeature);
-            mapUtil.getMap().fitBounds(featureGroup.getBounds(), { padding: [25, 25] });
+            mapUtil
+              .getMap()
+              .fitBounds(featureGroup.getBounds(), { padding: [25, 25] });
           }
         })
         .catch((error) => {
diff --git a/src/views/feature/Feature_edit.vue b/src/views/feature/Feature_edit.vue
index 985096d769e26bf34bd5d4a86e0a59245159dc90..978f73da054d5e111c1b53f7e66ba0ce044a00d1 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,41 +84,66 @@
               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">
-                  {{ 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">
+                        {{ 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"
@@ -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"
         >
@@ -367,6 +392,10 @@ export default {
       );
     },
 
+    orderedCustomFields() {
+      return [...this.extra_form].sort((a, b) => a.position - b.position);
+    },
+
     geoRefFileLabel() {
       if (this.file) {
         return this.file.name;
@@ -458,6 +487,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) {
@@ -471,7 +504,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];
     },
 
@@ -481,19 +524,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: {
@@ -502,25 +543,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 (error) {
-          if (error && error.response && error.response) {
-            self.erreurUploadMessage = error.response.data.error;
-          } else {
-            self.erreurUploadMessage =
-              "Une erreur est survenue pendant l'import de l'image géoréférencée";
-          }
+        .catch((response) => {
+          console.log("FAILURE!!");
+          this.erreurUploadMessage = response.data.message;
         });
     },
 
@@ -960,7 +997,7 @@ export default {
         this.initForm();
         this.initMap();
         this.onFeatureTypeLoaded();
-        this.initExtraForms();
+        this.initExtraForms(this.feature);
 
         setTimeout(
           function () {
@@ -1010,4 +1047,9 @@ export default {
 .error-message {
   color: red;
 }
+/* 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..f5d4b5ba5abd4560312d060de958d326739424b2 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="
@@ -199,7 +198,6 @@ export default {
   data() {
     return {
       modalAllDeleteOpen: false,
-      checkedFeatures: [],
       form: {
         type: {
           selected: null,
@@ -233,7 +231,6 @@ export default {
       },
 
       geojsonFeatures: [],
-      featureLoading: false,
       filterStatus: null,
       filterType: null,
       baseUrl: this.$store.state.configuration.BASE_URL,
@@ -249,7 +246,7 @@ export default {
   computed: {
     ...mapGetters(["project", "permissions"]),
     ...mapState(["user"]),
-    ...mapState("feature", ["features"]),
+    ...mapState("feature", ["features", "checkedFeatures"]),
     ...mapState("feature_type", ["feature_types"]),
 
     baseMaps() {
@@ -369,15 +366,18 @@ export default {
         this.loadFeatures(this.$store.state.map.geojsonFeatures);
       } else {
         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;
+            this.$store.commit("DISCARD_LOADER");
           })
           .catch((error) => {
-            this.featureLoading = false;
+            this.$store.commit("DISCARD_LOADER");
             throw error;
           });
       }
@@ -429,6 +429,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_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