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..26aaa046a456842652f0a609ec0335b835715e56 100644
--- a/package.json
+++ b/package.json
@@ -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/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/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/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/FeatureType/FeatureTypeDetail.vue b/src/views/FeatureType/FeatureTypeDetail.vue
index 2c20b6c90632cacfd92a6942e2b0539e14cb7a9c..389aeae9eae8ba16ed58aacaf566c1a99015e003 100644
--- a/src/views/FeatureType/FeatureTypeDetail.vue
+++ b/src/views/FeatureType/FeatureTypeDetail.vue
@@ -352,12 +352,14 @@
 </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 +541,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 +552,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 +578,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 +603,7 @@ export default {
         return false;
       }
       const sampleLine =
-        csv
+        csvString
           .split('\n')[1]
           .split(delimiter)
           .map(el => {
@@ -630,55 +611,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>