diff --git a/src/components/Dropdown.vue b/src/components/Dropdown.vue index bcd926b46eab7eca20af5699a6c0161556d9b725..4e94c07314c3a972219fceb968213ca4e8f17708 100644 --- a/src/components/Dropdown.vue +++ b/src/components/Dropdown.vue @@ -103,4 +103,10 @@ export default { window.removeEventListener("mousedown", this.clickOutsideDropdown); }, }; -</script> \ No newline at end of file +</script> + +<style scoped> +.ui.selection.dropdown .menu > .item { + white-space: nowrap; +} +</style> \ No newline at end of file diff --git a/src/components/feature_type/FeatureTypeCustomForm.vue b/src/components/feature_type/FeatureTypeCustomForm.vue index 693af72eab27ff6dfa9f3c7ab6e1d7f0d9d38ced..1e3b18a85808c12c206ea126a1c189ce3b4791cb 100644 --- a/src/components/feature_type/FeatureTypeCustomForm.vue +++ b/src/components/feature_type/FeatureTypeCustomForm.vue @@ -66,7 +66,7 @@ }}</label> <Dropdown :disabled="!form.label.value || !form.name.value" - :options="form.field_type.field.choices" + :options="fieldTypeChoices" :selected="selected" :selection.sync="selected" /> @@ -81,9 +81,8 @@ :maxlength="form.options.field.max_length" :name="form.options.html_name" :id="form.options.id_for_label" - v-model="form.options.value" + v-model="arrayOption" class="options-field" - @input="updateOptions" /> <small>{{ form.help_text }}</small> {{ form.options.errors }} @@ -108,22 +107,17 @@ export default { props: ["customForm"], - computed: { - selected: { - // getter - get() { - return this.form.field_type.value; - }, - // setter - set(newValue) { - this.form.field_type.value = newValue; - this.updateStore(); - }, - }, - }, - data() { return { + fieldTypeChoices: [ + { name: "Booléen", value: "boolean" }, + { name: "Chaîne de caractères", value: "char" }, + { name: "Date", value: "date" }, + { name: "Liste de valeurs", value: "list" }, + { name: "Nombre entier", value: "integer" }, + { name: "Nombre décimal", value: "decimal" }, + { name: "Texte multiligne", value: "text" }, + ], form: { label: { errors: null, @@ -169,17 +163,8 @@ export default { help_text: "", field: { max_length: 50, - choices: [ - "Booléen", - "Chaîne de caractères", - "Date", - "Liste de valeurs", - "Nombre entier", - "Nombre décimal", - "Texte multiligne", - ], }, - value: null, + value: null, //* field to send to the backend }, options: { errors: null, @@ -190,11 +175,43 @@ export default { field: { max_length: 256, }, - value: null, + value: [], }, }, }; }, + + computed: { + selected: { + // getter + get() { + const currentFieldType = this.fieldTypeChoices.find( + (el) => el.value === this.form.field_type.value + ); + if (currentFieldType) { + return currentFieldType.name; + } + return null; + }, + // setter + set(newValue) { + this.form.field_type.value = newValue.value; + this.form = { ...this.form }; // ! quick & dirty fix for getter not updating because of Vue caveat https://vuejs.org/v2/guide/reactivity.html#For-Objects + // Vue.set(this.form.field_type, "value", newValue.value); // ? vue.set didn't work, maybe should flatten form ? + this.updateStore(); + }, + }, + arrayOption: { // * because backend expects an array + get() { + return [this.form.options.value] + }, + set(newValue) { + this.form.options.value = [newValue]; + this.updateOptions() + } + } + }, + methods: { removeCustomForm() { this.$store.commit( @@ -217,14 +234,16 @@ export default { this.$store.commit("feature_type/UPDATE_COLOR_STYLE"); }, }, - // beforeDestroy(){ - // this.$store.commit("feature_type/EMPTY_CUSTOM_FORM"); - - // }, + beforeDestroy(){ + this.$store.commit("feature_type/EMPTY_CUSTOM_FORMS"); + }, mounted() { for (let el in this.customForm) { - if (el && this.form[el]) this.form[el].value = this.customForm[el].value; + if (el && this.form[el] && this.customForm[el]) { + this.form[el].value = this.customForm[el].value; + } } + this.updateStore(); }, }; </script> diff --git a/src/store/modules/feature_type.js b/src/store/modules/feature_type.js index e4fdf2cbf93a7718d7f4c5c2236a9f302093caca..954199b3f254d44d6122dc5e913418264519e3aa 100644 --- a/src/store/modules/feature_type.js +++ b/src/store/modules/feature_type.js @@ -4,7 +4,7 @@ import axios from "axios" // export const RESET = 'RESET'; -/* const initialState = () => ({ +/* const initialState = () => ({ // ? closure ? form: null, colorsStyleList: [], customForms: [], @@ -59,6 +59,9 @@ const feature_type = { }, SET_IMPORT_FEATURE_TYPES_DATA(state, payload) { state.importFeatureTypeData = payload; + }, + EMPTY_CUSTOM_FORMS(state) { + state.customForms = []; } }, getters: { @@ -81,11 +84,11 @@ const feature_type = { //'project': state.form.project.value, 'customfield_set': state.customForms.map(el => { return { - 'position': el.position.value, - 'label': el.label.value, - 'name': el.name.value, - 'field_type': el.field_type.value, - 'options': el.options.value, + 'position': el.position, + 'label': el.label, + 'name': el.name, + 'field_type': el.field_type, + 'options': el.options, } }), //'is_editable': true, @@ -96,26 +99,21 @@ const feature_type = { .post(`${process.env.VUE_APP_DJANGO_API_BASE}feature-types/`, data) .then((response) => { console.log(response) - // const routerHistory = this.$router.options.routerHistory - //store.commit("SET_USER", response.data.user); - // this.$router.push(routerHistory[routerHistory.length - 1] || "/") - //store.dispatch("GET_USER_LEVEL_PROJECTS"); }) .catch((error) => { console.error(error); - // store.commit("SET_USER", false) }); }, - POST_FEATURES_FROM_GEOJSON({ rootGetters, dispatch }, payload) { - const { feature_type_slug, filenameToImport } = payload + POST_FEATURES_FROM_GEOJSON({ dispatch }, payload) { + const { slug, feature_type_slug, filenameToImport } = payload if (filenameToImport.size > 0) { var formData = new FormData(); formData.append("json_file", filenameToImport); let url = process.env.VUE_APP_URL_BASE + - "projet/" + rootGetters.project.slug + + "projet/" + slug + "/type-signalement/" + feature_type_slug + "/importer-geojson/"; axios @@ -146,7 +144,6 @@ const feature_type = { .get(url) .then((response) => { commit("SET_IMPORT_FEATURE_TYPES_DATA", response.data); - //commit("SET_IMPORT_TASK_DATA", response.data); }) .catch((err) => { console.log(err); diff --git a/src/views/feature_type/Feature_type_detail.vue b/src/views/feature_type/Feature_type_detail.vue index 4f251e2b7056ee282bb3cff4677e3e443f0b59ed..311c5ee7f246bbe93d41f446af176f09971df2ec 100644 --- a/src/views/feature_type/Feature_type_detail.vue +++ b/src/views/feature_type/Feature_type_detail.vue @@ -209,11 +209,7 @@ export default { watch: { structure(newVal, oldVal) { if (newVal !== oldVal) { - //this.getImports(); - this.$store.dispatch( - "feature_type/GET_IMPORTS", - this.structure.slug - ); + this.$store.dispatch("feature_type/GET_IMPORTS", this.structure.slug); } }, }, @@ -227,7 +223,6 @@ export default { fileToImport: {}, showImport: false, showExport: true, - //dataImport: [], }; }, @@ -240,54 +235,11 @@ export default { importGeoJson() { this.$store.dispatch("feature_type/POST_FEATURES_FROM_GEOJSON", { - //slug: this.$route.params.slug, + slug: this.$route.params.slug, feature_type_slug: this.$route.params.feature_type_slug, filenameToImport: this.filenameToImport, }); - /* if (this.filenameToImport.size > 0) { - var formData = new FormData(); - formData.append("json_file", this.filenameToImport); - let url = - process.env.VUE_APP_URL_BASE + - "projet/" + - this.$route.params.slug + - "/type-signalement/" + - this.$route.params.feature_type_slug + - "/importer-geojson/"; - axios - .post(url, formData, { - headers: { - "Content-Type": "multipart/form-data", - }, - }) - .then((response) => { - if (response.status == 200) { - this.getImports(); - // TODO : RELOAD DERNIER SIGNALEMENTS - } - }) - .catch((err) => { - // TODO : HANDLER ERROR - console.log(err); - }); - } */ }, - - /* getImports() { - let url = - process.env.VUE_APP_DJANGO_API_BASE + - "import-tasks?feature_type_id=" + - this.structure.feature_type; - axios - .get(url) - .then((response) => { - this.dataImport = response.data; - }) - .catch((err) => { - console.log(err); - }); - }, */ - exportFeatures() { console.log("TEST", this.$store); this.$store.dispatch( diff --git a/src/views/feature_type/Feature_type_edit.vue b/src/views/feature_type/Feature_type_edit.vue index c40c7c879b946bcada7a277ba35226f2101d29bd..f7a80d8e23873854e114e62b49d2ab484767b9e3 100644 --- a/src/views/feature_type/Feature_type_edit.vue +++ b/src/views/feature_type/Feature_type_edit.vue @@ -32,7 +32,7 @@ v-model="form.title.value" @blur="updateStore" /> - <ul class="errorlist"> + <ul id="errorlist" class="errorlist"> <li v-for="error in form.title.errors" :key="error"> {{ error }} </li> @@ -43,9 +43,9 @@ form.geom_type.label }}</label> <Dropdown - :options="form.geom_type.field.choices" - :selected="selected_geom_type" - :selection.sync="selected_geom_type" + :options="geomTypeChoices" + :selected="selectedGeomType" + :selection.sync="selectedGeomType" /> <!-- {{ form.geom_type.errors }} --> </div> @@ -156,6 +156,11 @@ export default { return { action: "create", dataKey: 0, + geomTypeChoices: [ + { value: "linestring", name: "Ligne" }, + { value: "point", name: "Point" }, + { value: "polygon", name: "Polygone" }, + ], form: { colors_style: { value: null, @@ -185,7 +190,7 @@ export default { id_for_label: "geom_type", label: "Type de géométrie", field: { - choices: ["Ligne", "Point", "Polygone"], + //choices: ["Ligne", "Point", "Polygone"], max_length: 128, // ! Vérifier la valeur dans django }, html_name: "geom_type", @@ -203,16 +208,6 @@ export default { "deletion_on", "feature_type", ], - - typeDict: { - boolean: "Booléen", - string: "Chaîne de caractères", - date: "Date", - list: "Liste de valeurs", - integer: "Nombre entier", - decimal: "Nombre décimal", - text: "Texte multiligne", - }, }; }, props: ["geojson"], @@ -221,12 +216,19 @@ export default { ...mapGetters(["project"]), ...mapState("feature_type", ["customForms", "colorsStyleList"]), ...mapGetters("feature_type", ["feature_type"]), - selected_geom_type: { + selectedGeomType: { get() { - return this.form.geom_type.value; + const currentGeomType = this.geomTypeChoices.find( + (el) => el.value === this.form.geom_type.value + ); + if (currentGeomType) { + return currentGeomType ? currentGeomType.name : null; + } + return null; }, set(newValue) { - this.form.geom_type.value = newValue; + this.form.geom_type.value = newValue.value; + this.form = { ...this.form }; // ! quick & dirty fix for getter not updating because of Vue caveat https://vuejs.org/v2/guide/reactivity.html#For-Objects this.updateStore(); }, }, @@ -275,7 +277,7 @@ export default { dataKey: this.dataKey, }; if (customForm) { - newCustomForm = { newCustomForm, ...customForm }; + newCustomForm = { ...newCustomForm, ...customForm }; } this.$store.commit("feature_type/ADD_CUSTOM_FORM", newCustomForm); // * create an object with the counter in store }, @@ -302,6 +304,9 @@ export default { !this.form.title.errors.includes("Veuillez compléter ce champ.") // TODO : Gérer les autres champs ) { this.form.title.errors.push("Veuillez compléter ce champ."); + document + .getElementById("errorlist") + .scrollIntoView({ block: "end", inline: "nearest" }); } }, postFeatureTypeNfeatures() { @@ -327,67 +332,59 @@ export default { }, translateLabel(value) { if (value == "LineString") { - return "Ligne"; + return "linestring"; } else if (value == "Polygon") { - return "Polygone"; + return "polygon"; } return "point"; - - //("linestring", "Ligne"), - //("point", "Point"), - //("polygon", "Polygone"), }, transformProperties(prop) { const type = typeof prop; + const date = new Date(prop); if (type === "boolean") { - return "Booléen"; + return "boolean"; } else if (type === "number") { - return "Nombre entier"; + return "integer"; } else if (type === "string") { - //* check if string is not convertible to a decimal - if (isNaN(parseFloat(prop))) { - if (new Date(prop) !== "Invalid Date") { - return "Date"; - } /* else if (prop.includes(",")) { - return "Liste de valeurs"; - } */ - else { - return "Chaîne de caractères"; - } - } else { - return "Nombre décimal"; + //* check if string is convertible to a number, then it should be a decimal + if (date instanceof Date && !isNaN(date.valueOf())) { + return "date"; + } else if (!isNaN(parseFloat(prop))) { + return "decimal"; } } - return null; - }, - - getFieldType(val) { - if (val in this.typeDict) return this.typeDict[val]; + return "char"; //* string by default, most accepted type in database }, importGeoJson() { - this.updateStore() // TODO : VALIDATION IF A JSONDICT HAS NOT FEATURES - if (this.geojson.features) { + if (this.geojson.features && this.geojson.features.length) { //* in order to get feature_type properties, the first feature is enough const { properties, geometry } = this.geojson.features[0]; 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 (to add) + //* 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 || "" }, // todo : increment position - position: { value: 0 }, // * not available in export + position: { value: this.dataKey }, // * use dataKey incremented at addCustomForm field_type: { value: this.transformProperties(val) }, // * guessed from the type - options: { value: null }, // * not available in export + options: { value: [] }, // * not available in export }; - console.log("customForm", customForm, this.transformProperties(val), val); + console.log( + "val", + val, + "transformProperties", + this.transformProperties(val) + ); this.addCustomForm(customForm); } } diff --git a/src/views/project/Project_detail.vue b/src/views/project/Project_detail.vue index d1c9a20685c1076491e2daa5fd69b751b510f9a9..2c6f54c65d08a139dce02c5524d671850b11688e 100644 --- a/src/views/project/Project_detail.vue +++ b/src/views/project/Project_detail.vue @@ -193,7 +193,8 @@ <label class="ui" for="json_file"> <i class="ui plus icon"></i> <span class="label" - >Créer un nouveau type de signalement à partir d'un GeoJSON</span + >Créer un nouveau type de signalement à partir d'un + GeoJSON</span > </label> <input @@ -486,8 +487,8 @@ export default { ...mapState(["last_comments", "user"]), BASE_URL: () => process.env.VUE_APP_BASE_URL, last_features: function () { - // TODO : Filter les dernières - return this.$store.state.feature.features; + // * limit to last five element of array (looks sorted chronologically, but not sure...) + return this.$store.state.feature.features.slice(-5); }, }, created() {