diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index bd01e22edd47c77eb35b09eff7b41198f1463721..8ae10fb420e5c853a1b2b129380a52dffff24a5c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -25,16 +25,14 @@ build testing docker image:
   only:
     - develop
   tags:
-    - build
-  image:
-    name: gcr.io/kaniko-project/executor:debug
-    entrypoint: [""]
+    - build_docker
+  variables:
+    DOCKER_TAG: testing
   script:
-    - mkdir -p /kaniko/.docker
-    - export
-    - echo "{\"auths\":{\"https://index.docker.io/v1/\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
-    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination neogeo/geocontrib-front:testing
-    - echo Image docker neogeo/geocontrib-front:testing livrée
+    - cat $DOCKER_PASSWORD | docker login --username $DOCKER_LOGIN --password-stdin
+    - docker-compose build geocontrib-front
+    - docker-compose push geocontrib-front
+    - echo Image docker neogeo/geocontrib-front:${DOCKER_TAG} livrée
 
 deploy testing docker image:
   stage: deploy
@@ -52,36 +50,30 @@ build stable docker image:
   only:
     - master
   tags:
-    - build
-  image:
-    name: gcr.io/kaniko-project/executor:debug
-    entrypoint: [""]
+    - build_docker
+  variables:
+    DOCKER_TAG: latest
   script:
-    - mkdir -p /kaniko/.docker
-    - echo "{\"auths\":{\"https://index.docker.io/v1/\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
-
-    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination neogeo/geocontrib-front:latest
-    - echo Image docker neogeo/geocontrib:latest livrée
-
+    - cat $DOCKER_PASSWORD | docker login --username $DOCKER_LOGIN --password-stdin
+    - docker-compose build geocontrib-front
+    - docker-compose push geocontrib-front
+    - echo Image docker neogeo/geocontrib-front:${DOCKER_TAG} livrée
 
 build tagged docker image:
   stage: build
   only:
     - tags
   tags:
-    - build
-  image:
-    name: gcr.io/kaniko-project/executor:debug
-    entrypoint: [""]
+    - build_docker
+  variables:
+    DOCKER_TAG: $CI_COMMIT_TAG
   script:
     # Don't build tag id package.json as wrong version
     - grep "\"version\":.\"$CI_COMMIT_TAG\"" package.json
-    - mkdir -p /kaniko/.docker
-    - echo "{\"auths\":{\"https://index.docker.io/v1/\":{\"auth\":\"$DOCKER_AUTH\"}}}" > /kaniko/.docker/config.json
-
-    - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination neogeo/geocontrib-front:$CI_COMMIT_TAG
-    - echo Image docker neogeo/geocontrib-front:$CI_COMMIT_TAG livrée
-
+    - cat $DOCKER_PASSWORD | docker login --username $DOCKER_LOGIN --password-stdin
+    - docker-compose build geocontrib-front
+    - docker-compose push geocontrib-front
+    - echo Image docker neogeo/geocontrib-front:${DOCKER_TAG} livrée
 
 sonarqube-check:
   image:
diff --git a/docker-compose.yaml b/docker-compose.yaml
index 18f164891441f5ecbbaa1bbf86ba056f826570f6..a250dbb007c4693ba0d4c4e033be50cc1ed6fa28 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -2,7 +2,7 @@
 version: "3"
 services:
   geocontrib-front:
-    image: neogeo/geocontrib-front:geocontrib-latest
+    image: neogeo/geocontrib-front:${DOCKER_TAG:-testing}
     build: .
     environment:
       - BASE_URL=${BASE_URL}
diff --git a/package-lock.json b/package-lock.json
index 0910a9aac51e1d483ac0dfd470833f00f33d1a22..d597b30628b53ba076f5b3085493f3d10e2a5a1c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "geocontrib-frontend",
-  "version": "3.0.2",
+  "version": "3.1.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -2562,6 +2562,16 @@
           "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
           "dev": true
         },
+        "ansi-styles": {
+          "version": "4.3.0",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-convert": "^2.0.1"
+          }
+        },
         "array-union": {
           "version": "1.0.2",
           "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
@@ -2571,6 +2581,34 @@
             "array-uniq": "^1.0.1"
           }
         },
+        "chalk": {
+          "version": "4.1.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
+          }
+        },
+        "color-convert": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "color-name": "~1.1.4"
+          }
+        },
+        "color-name": {
+          "version": "1.1.4",
+          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+          "dev": true,
+          "optional": true
+        },
         "dir-glob": {
           "version": "2.2.2",
           "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-2.2.2.tgz",
@@ -2642,6 +2680,13 @@
             "slash": "^2.0.0"
           }
         },
+        "has-flag": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true,
+          "optional": true
+        },
         "ignore": {
           "version": "4.0.6",
           "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
@@ -2679,6 +2724,28 @@
           "requires": {
             "minipass": "^3.1.1"
           }
+        },
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "vue-loader-v16": {
+          "version": "npm:vue-loader@16.8.3",
+          "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
+          "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
+          "dev": true,
+          "optional": true,
+          "requires": {
+            "chalk": "^4.1.0",
+            "hash-sum": "^2.0.0",
+            "loader-utils": "^2.0.0"
+          }
         }
       }
     },
@@ -3561,8 +3628,7 @@
     "bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
-      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
-      "dev": true
+      "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg=="
     },
     "bn.js": {
       "version": "5.2.0",
@@ -5207,6 +5273,16 @@
         }
       }
     },
+    "csvtojson": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmjs.org/csvtojson/-/csvtojson-2.0.10.tgz",
+      "integrity": "sha512-lUWFxGKyhraKCW8Qghz6Z0f2l/PqB1W3AO0HKJzGIQ5JRSlR651ekJDiGJbBT4sRNNv5ddnSGVEnsxP9XRCVpQ==",
+      "requires": {
+        "bluebird": "^3.5.1",
+        "lodash": "^4.17.3",
+        "strip-bom": "^2.0.0"
+      }
+    },
     "cyclist": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -8321,6 +8397,11 @@
       "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
       "dev": true
     },
+    "is-utf8": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz",
+      "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q=="
+    },
     "is-weakref": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz",
@@ -12273,6 +12354,14 @@
         }
       }
     },
+    "strip-bom": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz",
+      "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==",
+      "requires": {
+        "is-utf8": "^0.2.0"
+      }
+    },
     "strip-comments": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-1.0.2.tgz",
@@ -13257,75 +13346,6 @@
         }
       }
     },
-    "vue-loader-v16": {
-      "version": "npm:vue-loader@16.8.3",
-      "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.3.tgz",
-      "integrity": "sha512-7vKN45IxsKxe5GcVCbc2qFU5aWzyiLrYJyUuMz4BQLKctCj/fmCa0w6fGiiQ2cLFetNcek1ppGJQDCup0c1hpA==",
-      "dev": true,
-      "optional": true,
-      "requires": {
-        "chalk": "^4.1.0",
-        "hash-sum": "^2.0.0",
-        "loader-utils": "^2.0.0"
-      },
-      "dependencies": {
-        "ansi-styles": {
-          "version": "4.3.0",
-          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
-          "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "color-convert": "^2.0.1"
-          }
-        },
-        "chalk": {
-          "version": "4.1.2",
-          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
-          "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "ansi-styles": "^4.1.0",
-            "supports-color": "^7.1.0"
-          }
-        },
-        "color-convert": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
-          "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "color-name": "~1.1.4"
-          }
-        },
-        "color-name": {
-          "version": "1.1.4",
-          "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
-          "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
-          "dev": true,
-          "optional": true
-        },
-        "has-flag": {
-          "version": "4.0.0",
-          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
-          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-          "dev": true,
-          "optional": true
-        },
-        "supports-color": {
-          "version": "7.2.0",
-          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
-          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-          "dev": true,
-          "optional": true,
-          "requires": {
-            "has-flag": "^4.0.0"
-          }
-        }
-      }
-    },
     "vue-multiselect": {
       "version": "2.1.6",
       "resolved": "https://registry.npmjs.org/vue-multiselect/-/vue-multiselect-2.1.6.tgz",
diff --git a/package.json b/package.json
index e354da93825ab0839f017a113dacfe453884496e..0ec65a9cb1fbf857590a549731a5ea76fd2682db 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "geocontrib-frontend",
-  "version": "3.1.0",
+  "version": "3.2.0",
   "private": true,
   "scripts": {
     "serve": "npm run init-proxy & npm run init-serve",
@@ -21,6 +21,7 @@
     "@turf/helpers": "^6.5.0",
     "axios": "^0.21.1",
     "core-js": "^3.20.2",
+    "csvtojson": "^2.0.10",
     "lodash": "^4.17.21",
     "ol": "6.8.1",
     "ol-mapbox-style": "^6.8.3",
diff --git a/src/App.vue b/src/App.vue
index b45820b6b21df309dee4b1368a6b29b23238866e..56520c47cf02a578d7bc5d8d768073dcd549b360 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -66,71 +66,4 @@ export default {
     ])
   },
 };
-</script>
-
-<style>
-.vertical {
-  flex-direction: column;
-  justify-content: center;
-}
-
-.leaflet-container {
-  background: white !important;
-}
-
-.flex {
-  display: flex;
-}
-
-/* keep above loader */
-#menu-dropdown {
-  z-index: 1001;
-}
-
-@media screen and (max-width: 985px) {
-  .abstract{
-    display: none !important;
-  }
-}
-
-@media screen and (min-width: 560px) {
-  .mobile {
-    display: none !important;
-  }
-  #app-header {
-    min-width: 560px;
-  }
-  .menu.container {
-    width: auto !important;
-  }
-  .push-right-desktop {
-    margin-left: auto;
-  }
-}
-
-@media screen and (max-width: 590px) {
-  .desktop {
-    display: none !important;
-  }
-  div.dropdown-list {
-    width: 100vw;
-    left: -70px !important; /* should be the same than belows */
-  }
-  .menu.container a.header {
-    width: 70px;
-  }
-  .menu.container a.header > img {
-    margin: 0;
-  }
-  #menu-dropdown {
-    width: calc(100vw - 70px);
-    justify-content: space-between;
-  }
-  #menu-dropdown > span {
-    text-overflow: ellipsis;
-    overflow: hidden;
-    white-space: nowrap;
-  }
-}
-
-</style>
+</script>
\ No newline at end of file
diff --git a/src/assets/js/utils.js b/src/assets/js/utils.js
index 0680fcdfad82586d6325e4cc501bf9873abf4d7c..85e7a1b11496402eb57d688f13dcd2d187a90665 100644
--- a/src/assets/js/utils.js
+++ b/src/assets/js/utils.js
@@ -12,30 +12,4 @@ export function fileConvertSizeToMo(aSize){
   aSize = Math.abs(parseInt(aSize, 10));
   const def = [1024*1024, 'Mo', 1];
   return (aSize/def[0]).toFixed(def[2]);
-}
-
-export function csvToJson(csv, delimiter) {
-  const result = [];
-
-  const allLines = csv.split('\n');
-  const headers = allLines[0].split(delimiter).map(el => {
-    return el.replace('\r', '');
-  });
-  const [, ...lines] = allLines;
-
-  for (const line of lines) {
-    if (line) {
-      const obj = {};
-      const currentLine = line.split(delimiter).map(el => {
-        return el.replace('\r', '');
-      });
-
-      for (let i = 0; i < headers.length; i++) {
-        obj[headers[i]] = currentLine[i];
-      }
-
-      result.push(obj);
-    }
-  }
-  return JSON.parse(JSON.stringify(result));
-}
+}
\ No newline at end of file
diff --git a/src/components/AppHeader.vue b/src/components/AppHeader.vue
index 77f4c4ac316b16c6c5882abf6238d3b3775ac210..ec2a5d07f5d4fa8812cd2b2d8da1dedcca000a10 100644
--- a/src/components/AppHeader.vue
+++ b/src/components/AppHeader.vue
@@ -22,8 +22,15 @@
           :class="['ui dropdown item', { 'active visible': menuIsOpen }]"
           @click="menuIsOpen = !menuIsOpen"
         >
-          <!-- empty span to occupy space for style if no project -->
-          <span>
+          <div
+            v-if="!isOnline"
+            class="crossed-out mobile"
+          >
+            <i
+              class="wifi icon"
+            />
+          </div>
+          <span class="expand-center">
             <span v-if="project"> Projet : {{ project.title }} </span>
           </span>
           <i
@@ -155,6 +162,20 @@
           </span>
         </div>
         <div class="desktop flex push-right-desktop">
+          <div
+            v-if="!isOnline"
+            class="item"
+          >
+            <span
+              data-tooltip="Vous êtes hors-ligne,
+                vos changements pourront être envoyés au serveur au retour de la connexion"
+              data-position="bottom right"
+            >
+              <div class="crossed-out">
+                <i class="wifi icon"/>
+              </div>
+            </span>
+          </div>
           <router-link
             :is="isOnline ? 'router-link' : 'span'"
             v-if="user"
@@ -301,6 +322,86 @@ export default {
 </script>
 
 <style lang="less" scoped>
+.vertical {
+  flex-direction: column;
+  justify-content: center;
+}
+
+.flex {
+  display: flex;
+}
+
+/* keep above loader */
+#menu-dropdown {
+  z-index: 1001;
+}
+
+.expand-center {
+  width: 100%;
+  text-align: center;
+}
+
+.crossed-out {
+  position: relative;
+  padding: .2em;
+  &::before {
+    content: "";
+    position: absolute;
+    top: 45%;
+    left: -8%;
+    width: 100%;
+    border-top: 2px solid #ee2e24;
+    transform: rotate(45deg);
+    box-shadow: 0px 0px 0px 1px #373636;
+    border-radius: 3px;
+  }
+}
+
+@media screen and (max-width: 985px) {
+  .abstract{
+    display: none !important;
+  }
+}
+
+@media screen and (min-width: 560px) {
+  .mobile {
+    display: none !important;
+  }
+  #app-header {
+    min-width: 560px;
+  }
+  .menu.container {
+    width: auto !important;
+  }
+  .push-right-desktop {
+    margin-left: auto;
+  }
+}
+
+@media screen and (max-width: 590px) {
+  .desktop {
+    display: none !important;
+  }
+  div.dropdown-list {
+    width: 100vw;
+    left: -70px !important; /* should be the same than belows */
+  }
+  .menu.container a.header {
+    width: 70px;
+  }
+  .menu.container a.header > img {
+    margin: 0;
+  }
+  #menu-dropdown {
+    width: calc(100vw - 70px);
+    //justify-content: space-between;
+  }
+  #menu-dropdown > span {
+    text-overflow: ellipsis;
+    overflow: hidden;
+    white-space: nowrap;
+  }
+}
 
 .menu.container {
   position: relative;
@@ -348,4 +449,4 @@ export default {
   height: 100% !important;
 }
 
-</style>
+</style>
\ No newline at end of file
diff --git a/src/components/Feature/Edit/FeatureExtraForm.vue b/src/components/Feature/Edit/FeatureExtraForm.vue
index 8d09d514788bca922e52c7a66dce5982522a5ec2..36f4130b18a0fb6a3503c35eb256e1ef209d3bd7 100644
--- a/src/components/Feature/Edit/FeatureExtraForm.vue
+++ b/src/components/Feature/Edit/FeatureExtraForm.vue
@@ -1,9 +1,7 @@
 <template>
-  <div
-    v-if="field && field.field_type === 'char'"
-  >
+  <div v-if="field && field.field_type === 'char'">
     <label
-      v-if="$route.name === 'editer-signalement'"
+      v-if="displayLabels"
       :for="field.name"
     >
       {{ field.label }}
@@ -17,11 +15,9 @@
     >
   </div>
 
-  <div
-    v-else-if="field && field.field_type === 'list'"
-  >
+  <div v-else-if="field && field.field_type === 'list'">
     <label
-      v-if="$route.name === 'editer-signalement'"
+      v-if="displayLabels"
       :for="field.name"
     >
       {{ field.label }}
@@ -32,11 +28,9 @@
       :selection.sync="selected_extra_form_list"
     />
   </div>
-  <div
-    v-else-if="field && field.field_type === 'integer'"
-  >
+  <div v-else-if="field && field.field_type === 'integer'">
     <label
-      v-if="$route.name === 'editer-signalement'"
+      v-if="displayLabels"
       :for="field.name"
     >
       {{ field.label }}
@@ -52,9 +46,7 @@
       >
     </div>
   </div>
-  <div
-    v-else-if="field && field.field_type === 'boolean'"
-  >
+  <div v-else-if="field && field.field_type === 'boolean'">
     <div class="ui checkbox">
       <input
         :id="field.name"
@@ -64,15 +56,13 @@
         @change="updateStore_extra_form"
       >
       <label :for="field.name">
-        {{ $route.name === 'editer-signalement' ? field.label : '' }}
+        {{ displayLabels ? field.label : '' }}
       </label>
     </div>
   </div>
-  <div
-    v-else-if="field && field.field_type === 'date'"
-  >
+  <div v-else-if="field && field.field_type === 'date'">
     <label
-      v-if="$route.name === 'editer-signalement'"
+      v-if="displayLabels"
       :for="field.name"
     >
       {{ field.label }}
@@ -85,11 +75,9 @@
       @blur="updateStore_extra_form"
     >
   </div>
-  <div
-    v-else-if="field && field.field_type === 'decimal'"
-  >
+  <div v-else-if="field && field.field_type === 'decimal'">
     <label
-      v-if="$route.name === 'editer-signalement'"
+      v-if="displayLabels"
       :for="field.name"
     >
       {{ field.label }}
@@ -105,11 +93,9 @@
       >
     </div>
   </div>
-  <div
-    v-else-if="field && field.field_type === 'text'"
-  >
+  <div v-else-if="field && field.field_type === 'text'">
     <label
-      v-if="$route.name === 'editer-signalement'"
+      v-if="displayLabels"
       :for="field.name"
     >
       {{ field.label }}
@@ -152,6 +138,10 @@ export default {
         this.$store.commit('feature/UPDATE_EXTRA_FORM', newExtraForm);
       },
     },
+
+    displayLabels() {
+      return this.$route.name === 'editer-signalement' || this.$route.name === 'ajouter-signalement';
+    }
   },
 
   methods: {
diff --git a/src/components/FeatureType/FeatureTypeCustomForm.vue b/src/components/FeatureType/FeatureTypeCustomForm.vue
index e71b17585637336eace2b1f6e7fe029e79f4afcc..51f068a1460a50452a0bd20da3def1f4302fb3ea 100644
--- a/src/components/FeatureType/FeatureTypeCustomForm.vue
+++ b/src/components/FeatureType/FeatureTypeCustomForm.vue
@@ -365,53 +365,46 @@ export default {
       return occurences.length === 1;
     },
 
-    checkFilledOptions() {
-      if (this.form.field_type.value === 'list') {
-        if (this.form.options.value.length < 1) {
-          return false;
-        } else if (
-          this.form.options.value.length === 1 &&
-          this.form.options.value[0] === ''
-        ) {
-          return false;
-        }
-      }
-      return true;
+    checkListOptions() {
+      if (this.form.field_type.value !== 'list') return true;
+      return this.form.options.value.length >= 2 && !this.form.options.value.includes('');
     },
 
     checkCustomForm() {
       this.form.label.errors = [];
       this.form.name.errors = [];
       this.form.options.errors = [];
+      let isValid = true;
       if (!this.form.label.value) {
         //* vérifier que le label est renseigné
         this.form.label.errors = ['Veuillez compléter ce champ.'];
-        return false;
+        isValid = false;
       } else if (!this.form.name.value) {
         //* vérifier que le nom est renseigné
         this.form.name.errors = ['Veuillez compléter ce champ.'];
-        return false;
+        isValid = 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.',
         ];
-        return false;
+        isValid = false;
       } 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;
-      } else if (!this.checkFilledOptions()) {
+        isValid = false;
+      } else if (!this.checkListOptions()) {
         //* s'il s'agit d'un type liste, vérifier que le champ option est bien renseigné
         this.form.options.errors = ['Veuillez compléter ce champ.'];
-        return false;
+        isValid = false;
       } else if (this.hasDuplicateOptions()) {
         //* pour le cas d'options dupliqués
-        return false;
+        isValid = false;
       }
-      return true;
+      if (!isValid) document.getElementById(`custom_form-${this.form.position.value}`).scrollIntoView({ block: 'start', inline: 'nearest' });
+      return isValid;
     },
   },
 };
diff --git a/src/components/Project/Detail/ProjectFeatureTypes.vue b/src/components/Project/Detail/ProjectFeatureTypes.vue
index fa88de3e66e19b648fc845c0f5258eff69964bc2..f98acb30e2b0eb3b59d8be7f9adca67a805e03a8 100644
--- a/src/components/Project/Detail/ProjectFeatureTypes.vue
+++ b/src/components/Project/Detail/ProjectFeatureTypes.vue
@@ -426,10 +426,11 @@
 </template>
 
 <script>
+import { csv } from 'csvtojson';
 
 import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
 
-import { fileConvertSizeToMo, csvToJson } from '@/assets/js/utils';
+import { fileConvertSizeToMo } from '@/assets/js/utils';
 import FeatureTypeLink from '@/components/FeatureType/FeatureTypeLink';
 
 export default {
@@ -665,7 +666,11 @@ export default {
                 .filter(Boolean);
             if (sampleLine.length > 1 && headersCoord.length === 2) {
               this.csvError = null;
-              this.csvImport = csvToJson(fr.result, delimiter);
+              csv()
+                .fromString(fr.result)
+                .then((jsonObj)=>{
+                  this.csvImport = jsonObj;
+                });
               this.featureTypeImporting = false;
               //* stock filename to import features afterward
               this.SET_FILE_TO_IMPORT(this.csvFileToImport);
diff --git a/src/components/Project/FeaturesListAndMap/FeatureListTable.vue b/src/components/Project/FeaturesListAndMap/FeatureListTable.vue
index 006a27a2bec416dd3789244d2abc3fef9a6a32ef..aa6aa2a8e9698a43156387298640995f4f72ed4a 100644
--- a/src/components/Project/FeaturesListAndMap/FeatureListTable.vue
+++ b/src/components/Project/FeaturesListAndMap/FeatureListTable.vue
@@ -1,7 +1,10 @@
 <template>
   <div>
     <div class="ui form">
-      <div class="inline fields">
+      <div
+        v-if="isOnline"
+        class="inline fields"
+      >
         <label
           data-tooltip="Choisir un type de sélection de signalements pour effectuer une action"
           data-position="bottom left"
@@ -57,6 +60,7 @@
         <thead>
           <tr>
             <th
+              v-if="isOnline"
               scope="col"
               class="dt-center"
             >
@@ -68,7 +72,7 @@
               class="dt-center"
             >
               <div
-                class="pointer"
+                :class="isOnline ? 'pointer' : 'disabled'"
                 @click="changeSort('status')"
               >
                 Statut
@@ -87,7 +91,7 @@
               class="dt-center"
             >
               <div
-                class="pointer"
+                :class="isOnline ? 'pointer' : 'disabled'"
                 @click="changeSort('feature_type')"
               >
                 Type
@@ -106,7 +110,7 @@
               class="dt-center"
             >
               <div
-                class="pointer"
+                :class="isOnline ? 'pointer' : 'disabled'"
                 @click="changeSort('title')"
               >
                 Nom
@@ -125,7 +129,7 @@
               class="dt-center"
             >
               <div
-                class="pointer"
+                :class="isOnline ? 'pointer' : 'disabled'"
                 @click="changeSort('updated_on')"
               >
                 Dernière modification
@@ -145,7 +149,7 @@
               class="dt-center"
             >
               <div
-                class="pointer"
+                :class="isOnline ? 'pointer' : 'disabled'"
                 @click="changeSort('display_creator')"
               >
                 Auteur
@@ -165,7 +169,7 @@
               class="dt-center"
             >
               <div
-                class="pointer"
+                :class="isOnline ? 'pointer' : 'disabled'"
                 @click="changeSort('display_last_editor')"
               >
                 Dernier éditeur
@@ -186,7 +190,10 @@
             v-for="(feature, index) in paginatedFeatures"
             :key="index"
           >
-            <td class="dt-center">
+            <td
+              v-if="isOnline"
+              class="dt-center"
+            >
               <div
                 :class="['ui checkbox', {disabled: !checkRights(feature)}]"
               >
@@ -310,7 +317,7 @@
         sur {{ featuresCount }} éléments
       </div>
       <div
-        v-if="pageNumbers.length > 1"
+        v-if="pageNumbers.length > 1 && isOnline"
         id="table-features_paginate"
         class="dataTables_paginate paging_simple_numbers"
       >
@@ -429,7 +436,11 @@ export default {
 
   computed: {
     ...mapGetters(['permissions']),
-    ...mapState(['user', 'USER_LEVEL_PROJECTS']),
+    ...mapState([
+      'user',
+      'USER_LEVEL_PROJECTS',
+      'isOnline'
+    ]),
     ...mapState('projects', ['project']),
     ...mapState('feature', ['clickedFeatures', 'massMode']),
 
@@ -556,6 +567,7 @@ export default {
     },
 
     changeSort(column) {
+      if (!this.isOnline) return;
       if (this.sort.column === column) {
         //changer only order
         this.$emit('update:sort', {
diff --git a/src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue b/src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue
index 763887b2f192dcc327a75dd45ad5cd0633a3ea7f..20c771d0d40474d6ec1d490cd36c0e4d09140a2b 100644
--- a/src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue
+++ b/src/components/Project/FeaturesListAndMap/FeaturesListAndMapFilters.vue
@@ -86,7 +86,7 @@
               </div>
             </div>
             <div
-              v-if="checkedFeatures.length > 0 && massMode.includes('edit')"
+              v-if="checkedFeatures.length > 0 && massMode.includes('edit') && isOnline"
               class="ui dropdown button compact button-hover-green tiny-margin-left"
               :data-tooltip="`Modifier le${massMode.includes('status') ? ' statut' : 's attributs'} des signalements`"
               data-position="bottom right"
@@ -117,7 +117,7 @@
               </div>
             </div>
             <div
-              v-if="checkedFeatures.length > 0 && massMode === 'delete-features'"
+              v-if="checkedFeatures.length > 0 && massMode === 'delete-features' && isOnline"
               class="ui button compact button-hover-red tiny-margin-left"
               data-tooltip="Supprimer tous les signalements sélectionnés"
               data-position="bottom right"
@@ -138,7 +138,7 @@
     >
       <div
         id="type"
-        class="field column"
+        :class="['field column', { 'disabled': !isOnline }]"
       >
         <label>Type</label>
         <Dropdown
@@ -151,7 +151,7 @@
       </div>
       <div
         id="statut"
-        class="field column"
+        :class="['field column', { 'disabled': !isOnline }]"
       >
         <label>Statut</label>
         <!--  //* giving an object mapped on key name -->
@@ -165,7 +165,7 @@
       </div>
       <div
         id="name"
-        class="field column"
+        :class="['field column', { 'disabled': !isOnline }]"
       >
         <label>Nom</label>
         <div class="ui icon input">
@@ -260,7 +260,8 @@ export default {
   computed: {
     ...mapState([
       'user',
-      'USER_LEVEL_PROJECTS'
+      'USER_LEVEL_PROJECTS',
+      'isOnline'
     ]),
     ...mapState('feature', [
       'checkedFeatures',
diff --git a/src/main.js b/src/main.js
index 0bae0e95ba2fa6a2b0778efdde6df71763a4fe88..cabb80a8ddc6293333db579cbb747d5852b9590b 100644
--- a/src/main.js
+++ b/src/main.js
@@ -26,7 +26,7 @@ Vue.config.productionTip = false;
 
 // gestion mise à jour du serviceWorker et du precache
 var refreshing=false;
-if(navigator.serviceWorker){
+if (navigator.serviceWorker) {
   navigator.serviceWorker.addEventListener('controllerchange', () => {
     // We'll also need to add 'refreshing' to our data originally set to false.
     if (refreshing) {
diff --git a/src/utils/index.js b/src/utils/index.js
index 432cd2970a33e4be66ba5293a3501b9e7f76b2d5..a89354b8305aaae4b1d12e04003990b7e25cda55 100644
--- a/src/utils/index.js
+++ b/src/utils/index.js
@@ -58,4 +58,29 @@ export function allowedStatus2change(user, isModerate, userStatus, isOwnFeature,
     }
   }
   return [];
+}
+
+export function transformProperties(prop) {
+  const type = typeof prop;
+  const date = new Date(prop);
+  const regInteger = /^-*?\d+$/;
+  const regFloat = /^-*?\d*?\.\d+$/;
+  const regText = /[\r\n]/;
+  if (type === 'boolean' || prop.toLowerCase() === 'true' || prop.toLowerCase() === 'False') {
+    return 'boolean';
+  } else if (regInteger.test(prop) || Number.isSafeInteger(prop)) {
+    return 'integer';
+  } else if (
+    type === 'string' &&
+    ['/', ':', '-'].some((el) => prop.includes(el)) && // check for chars found in datestring
+    date instanceof Date &&
+    !isNaN(date.valueOf())
+  ) {
+    return 'date';
+  } else if (regFloat.test(prop) ||  type === 'number' && !isNaN(parseFloat(prop))) {
+    return 'decimal';
+  } else if (regText.test(prop)) {
+    return 'text';
+  }
+  return 'char'; //* string by default, most accepted type in database
 }
\ No newline at end of file
diff --git a/src/views/Feature/FeatureEdit.vue b/src/views/Feature/FeatureEdit.vue
index 6059ea53eff334338c572a54415fadaac7b9905b..35eaa28cc17c4b6e0c15fdfb9ed527e20c95f8b0 100644
--- a/src/views/Feature/FeatureEdit.vue
+++ b/src/views/Feature/FeatureEdit.vue
@@ -264,11 +264,11 @@
       <div
         v-for="(field, index) in orderedCustomFields"
         :key="field.field_type + index"
-        class="field"
       >
         <FeatureExtraForm
           :id="field.label"
           :field="field"
+          class="field"
         />
         {{ field.errors }}
       </div>
diff --git a/src/views/FeatureType/FeatureTypeDetail.vue b/src/views/FeatureType/FeatureTypeDetail.vue
index 2c20b6c90632cacfd92a6942e2b0539e14cb7a9c..97db52d8babd1925b82899592d1a4b3ee069e619 100644
--- a/src/views/FeatureType/FeatureTypeDetail.vue
+++ b/src/views/FeatureType/FeatureTypeDetail.vue
@@ -40,10 +40,12 @@
                 </div>
               </div>
               <div class="value">
-                {{ features_count }}
+                {{ isOnline ? features_count : '?' }}
               </div>
-              <div class="label">
-                Signalement{{ features.length > 1 ? "s" : "" }}
+              <div
+                class="label"
+              >
+                Signalement{{ features.length > 1 || !isOnline ? "s" : "" }}
               </div>
             </div>
 
@@ -221,7 +223,10 @@
         </div>
       </div>
 
-      <div class="nine wide column">
+      <div
+        v-if="isOnline"
+        class="nine wide column"
+      >
         <h3 class="ui header">
           Derniers signalements
         </h3>
@@ -347,17 +352,32 @@
         </router-link>
         <br>
       </div>
+      <div
+        v-else
+        class="nine wide column"
+      >
+        <h3 class="ui header">
+          Derniers signalements
+        </h3>
+        <div class="ui message info">
+          <p>
+            Information non disponible en mode déconnecté.
+          </p>
+        </div>
+      </div>
     </div>
   </div>
 </template>
 
 <script>
+import { csv } from 'csvtojson';
+
 import { mapActions, mapMutations, mapGetters, mapState } from 'vuex';
-import { formatStringDate } from '@/utils';
+import { formatStringDate, transformProperties } from '@/utils';
 import ImportTask from '@/components/ImportTask';
 import featureAPI from '@/services/feature-api';
 
-import { fileConvertSizeToMo, csvToJson } from '@/assets/js/utils';
+import { fileConvertSizeToMo } from '@/assets/js/utils'; // TODO: refactor with above utils, those files are similar
 
 export default {
   name: 'FeatureTypeDetail',
@@ -539,26 +559,6 @@ export default {
       }
     },
 
-    transformProperties(prop) {
-      const type = typeof prop;
-      const date = new Date(prop);
-      if (type === 'boolean') {
-        return 'boolean';
-      } else if (Number.isSafeInteger(prop)) {
-        return 'integer';
-      } else if (
-        type === 'string' &&
-        ['/', ':', '-'].some((el) => prop.includes(el)) && // check for chars found in datestring
-        date instanceof Date &&
-        !isNaN(date.valueOf())
-      ) {
-        return 'char';
-      } else if (type === 'number' && !isNaN(parseFloat(prop))) {
-        return 'decimal';
-      }
-      return 'char'; //* string by default, most accepted type in database
-    },
-
     checkJsonValidity(json) {
       this.importError = '';
       const fields = this.structure.customfield_set.map((el) => {
@@ -570,9 +570,10 @@ export default {
       });
       for (const feature of json.features) {
         for (const { name, field_type, options } of fields) {
-          if (name in feature.properties) {
-            const fieldInFeature = feature.properties[name];
-            const customType = this.transformProperties(fieldInFeature);
+          const properties = feature.properties || feature;
+          if (name in properties) {
+            const fieldInFeature = properties[name];
+            const customType = transformProperties(fieldInFeature);
             //* if custom field value is not null, then check validity of field
             if (fieldInFeature !== null) {
               //* if field type is list, it's not possible to guess from value type
@@ -595,22 +596,20 @@ export default {
       return true;
     },
 
-    checkCsvValidity(csv) {
+    async checkCsvValidity(csvString) {
       this.importError = '';
-
-      // Find csv delimiter
-      const commaDelimited = csv.split('\n')[0].includes(',');
-      const semicolonDelimited = csv.split('\n')[0].includes(';');
+      // Find csvString delimiter
+      const commaDelimited = csvString.split('\n')[0].includes(',');
+      const semicolonDelimited = csvString.split('\n')[0].includes(';');
       const delimiter = commaDelimited && !semicolonDelimited ? ',' : semicolonDelimited ?  ';' : false;
 
       if ((commaDelimited && semicolonDelimited) || !delimiter) {
         this.importError = `Le fichier ${this.csvFileToImport.name} n'est pas formaté correctement`;
         return false;
       }
-
       // Check if file contains 'lat' and 'long' fields
       const headersLine =
-        csv
+        csvString
           .split('\n')[0]
           .replace(/(\r\n|\n|\r)/gm, '')
           .split(delimiter)
@@ -622,7 +621,7 @@ export default {
         return false;
       }
       const sampleLine =
-        csv
+        csvString
           .split('\n')[1]
           .split(delimiter)
           .map(el => {
@@ -630,55 +629,8 @@ export default {
           })
           .filter(Boolean);
       if (sampleLine.length > 1 && headersLine.length === 2) {
-        const fields = this.structure.customfield_set.map((el) => {
-          return {
-            name: el.name,
-            field_type: el.field_type,
-            options: el.options,
-          };
-        });
-        const csvFeatures = csvToJson(csv, delimiter);
-        for (const feature of csvFeatures) {
-          for (let { name, field_type, options } of fields) {
-            if (name in feature) {
-              const fieldInFeature = feature[name];
-
-              // overide some specific cases on date type data
-              if (
-                typeof fieldInFeature === 'string' &&
-                ['/', ':', '-'].some((el) => fieldInFeature.includes(el)) &&
-                (new Date(fieldInFeature)) instanceof Date &&
-                !isNaN((new Date(fieldInFeature)).valueOf())
-              ) {
-                field_type = 'char';
-              } else if (
-                field_type === 'date' &&
-                ((new Date(fieldInFeature)) instanceof Date)
-              ) {
-                field_type = 'char';
-              }
-
-              const customType = this.transformProperties(fieldInFeature);
-              //* if custom field value is not null, then check validity of field
-              if (fieldInFeature !== null) {
-                //* if field type is list, it's not possible to guess from value type
-                if (field_type === 'list') {
-                  //*then check if the value is an available option
-                  if (fieldInFeature && !options.includes(fieldInFeature)) {
-                    this.importError = `Le fichier est invalide: la valeur [ ${fieldInFeature} ] n'est pas une option valide 
-                      pour le champ personnalisé "${name}".`;
-                    return false;
-                  }
-                } else if (customType !== field_type) {
-                  //* check if custom field value match
-                  this.importError = `Le fichier est invalide: Un champ de type ${field_type} ne peut pas avoir la valeur [ ${fieldInFeature} ]`;
-                  return false;
-                }
-              }
-            }
-          }
-        }
-        return true;
+        const features = await csv().fromString(csvString);
+        return this.checkJsonValidity({ features });
       } else {
         return false;
       }
diff --git a/src/views/FeatureType/FeatureTypeEdit.vue b/src/views/FeatureType/FeatureTypeEdit.vue
index 315acba70feeaf31c5d1beb13cbc259fbb08a0a3..47bad6dceb3668063f9c76a1b9de51d7c13fdc7c 100644
--- a/src/views/FeatureType/FeatureTypeEdit.vue
+++ b/src/views/FeatureType/FeatureTypeEdit.vue
@@ -159,6 +159,7 @@ import { mapGetters, mapState, mapMutations, mapActions } from 'vuex';
 
 import Dropdown from '@/components/Dropdown.vue';
 import FeatureTypeCustomForm from '@/components/FeatureType/FeatureTypeCustomForm.vue';
+import { transformProperties } from'@/utils';
 
 export default {
   name: 'FeatureTypeEdit',
@@ -639,23 +640,21 @@ export default {
       return 'point';
     },
 
-    transformProperties(prop) {
-      const type = typeof prop;
-      const date = new Date(prop);
-      if (type === 'boolean') {
-        return 'boolean';
-      } else if (Number.isSafeInteger(prop)) {
-        return 'integer';
-      } else if (
-        type === 'string' &&
-        date instanceof Date &&
-        !isNaN(date.valueOf())
-      ) {
-        return 'date';
-      } else if (type === 'number' && !isNaN(parseFloat(prop))) {
-        return 'decimal';
+    buildCustomForm(properties) {
+      for (const [key, val] of Object.entries(properties)) {
+        //* check that the property is not a keyword from the backend or map style
+        // todo: add map style keywords
+        if (!this.reservedKeywords.includes(key)) {
+          const customForm = {
+            label: { value: key || '' },
+            name: { value: key || '' },
+            position: this.dataKey, // * use dataKey already incremented by addCustomForm
+            field_type: { value: transformProperties(val) }, // * guessed from the type
+            options: { value: [] }, // * not available in export
+          };
+          this.addCustomForm(customForm);
+        }
       }
-      return 'char'; //* string by default, most accepted type in database
     },
 
     importGeoJsonFeatureType() {
@@ -665,93 +664,16 @@ export default {
         this.form.title.value = properties.feature_type;
         this.form.geom_type.value = this.translateLabel(geometry.type);
         this.updateStore(); //* register title & geom_type in store
-
-        //* loop properties to create a customForm for each of them
-        for (const [key, val] of Object.entries(properties)) {
-          //* check that the property is not a keyword from the backend or map style
-          // todo: add map style keywords
-          if (!this.reservedKeywords.includes(key)) {
-            const customForm = {
-              label: { value: key || '' },
-              name: { value: key || '' },
-              position: this.dataKey, // * use dataKey already incremented by addCustomForm
-              field_type: { value: this.transformProperties(val) }, // * guessed from the type
-              options: { value: [] }, // * not available in export
-            };
-            this.addCustomForm(customForm);
-          }
-        }
+        this.buildCustomForm(properties);
       }
     },
 
     importCSVFeatureType() {
       if (this.csv.length) {
         this.updateStore(); //* register title & geom_type in store
-        // List fileds for user to select coords fields
-        // this.csvFields =
-        //   Object.keys(this.csv[0])
-        //     .map(el => {
-        //       return {
-        //         field: el,
-        //         x: false,
-        //         y:false
-        //       };
-        //     });
-        for (const [key, val] of Object.entries(this.csv[0])) {
-          //* check that the property is not a keyword from the backend or map style
-          // todo: add map style keywords
-          if (!this.reservedKeywords.includes(key)) {
-            const customForm = {
-              label: { value: key || '' },
-              name: { value: key || '' },
-              position: this.dataKey, // * use dataKey already incremented by addCustomForm
-              field_type: { value: this.transformProperties(val) }, // * guessed from the type
-              options: { value: [] }, // * not available in export
-            };
-            this.addCustomForm(customForm);
-          }
-        }
+        this.buildCustomForm(this.csv[0]);
       }
     },
-
-    // pickXcsvCoordField(e) {
-    //   this.csvFields.forEach(el => {
-    //     if (el.field === e.field) {
-    //       el.x = true;
-    //     } else {
-    //       el.x = false;
-    //     }
-    //   });
-    // },
-    // pickYcsvCoordField(e) {
-    //   this.csvFields.forEach(el => {
-    //     if (el.field === e.field) {
-    //       el.y = true;
-    //     } else {
-    //       el.y = false;
-    //     }
-    //   });
-    // },
-    // setCSVCoordsFields() {
-    //   const xField = this.csvFields.find(el => el.x === true).field;
-    //   const yField = this.csvFields.find(el => el.y === true).field;
-    //   this.csvFields = null;
-
-    //   for (const [key, val] of Object.entries(this.csv[0])) {
-    //     //* check that the property is not a keyword from the backend or map style
-    //     // todo: add map style keywords
-    //     if (!this.reservedKeywords.includes(key) && key !== xField && key !== yField) {
-    //       const customForm = {
-    //         label: { value: key || '' },
-    //         name: { value: key || '' },
-    //         position: this.dataKey, // * use dataKey already incremented by addCustomForm
-    //         field_type: { value: this.transformProperties(val) }, // * guessed from the type
-    //         options: { value: [] }, // * not available in export
-    //       };
-    //       this.addCustomForm(customForm);
-    //     }
-    //   }
-    // }
   },
 };
 </script>
diff --git a/src/views/Project/FeaturesListAndMap.vue b/src/views/Project/FeaturesListAndMap.vue
index 897fd9ff8e285317d1be6ccd85efabe141aafe55..43eb47962ce31760671be3539af681dd03218551 100644
--- a/src/views/Project/FeaturesListAndMap.vue
+++ b/src/views/Project/FeaturesListAndMap.vue
@@ -14,7 +14,7 @@
       />
 
       <div
-        :class="['ui tab active map-container', {visible: showMap}]"
+        :class="['ui tab active map-container', { 'visible': showMap }]"
         data-tab="map"
       >
         <div