Newer
Older
Sébastien DA ROCHA
committed
<template>

Timothee P
committed
:class="['field', { 'disabled': field.disabled }]"
:data-field_type="field.field_type"
data-test="extra-form"

Timothee P
committed
<div
v-if="field && field.field_type === 'boolean'"
:class="['ui checkbox', { 'disabled': field.disabled }]"
<!-- JSON.parse is used in case of receiving a string 'true' or 'false'-->

Timothee P
committed
<input
:id="field.name"
type="checkbox"
:checked="JSON.parse(field.value)"

Timothee P
committed
:name="field.name"
@change="updateStore_extra_form"
>
<label :for="field.name">
{{ displayLabels ? field.label : '' }}
</label>
</div>

Timothee P
committed
<template v-else>
<label
v-if="displayLabels"
:for="field.name"
:class="{ required: field.is_mandatory }"
>
{{ field.label }}
</label>

Timothee P
committed
<input
v-if="field && field.field_type === 'char'"
:id="field.name"
:value="field.value"
type="text"
:name="field.name"
:required="field.is_mandatory"
@blur="updateStore_extra_form"

Timothee P
committed
<textarea
v-else-if="field && field.field_type === 'text'"
:value="field.value"
:name="field.name"
:required="field.is_mandatory"
rows="3"
@blur="updateStore_extra_form"
/>
Sébastien DA ROCHA
committed
<input

Timothee P
committed
v-else-if="field && field.field_type === 'integer'"
Sébastien DA ROCHA
committed
:id="field.name"
:value="field.value"
Sébastien DA ROCHA
committed
@change="updateStore_extra_form"
Sébastien DA ROCHA
committed
<input

Timothee P
committed
v-else-if="field && field.field_type === 'decimal'"

Timothee P
committed
:value="field.value"
type="number"
step=".01"
Sébastien DA ROCHA
committed
:name="field.name"

Timothee P
committed
:required="field.is_mandatory"
Sébastien DA ROCHA
committed
@change="updateStore_extra_form"
Sébastien DA ROCHA
committed
<input

Timothee P
committed
v-else-if="field && field.field_type === 'date'"
:value="field.value"

Timothee P
committed
type="date"
Sébastien DA ROCHA
committed
:name="field.name"
Sébastien DA ROCHA
committed
@change="updateStore_extra_form"

Timothee P
committed
<Dropdown
v-else-if="field && field.field_type === 'list'"
:options="field.options"
:selected="selected_extra_form_list"
:selection.sync="selected_extra_form_list"
:required="field.is_mandatory"
:search="true"
:clearable="true"
/>
<div
v-else-if="field && field.field_type === 'multi_choices_list'"
class="checkbox_list"
>
<div
v-for="option in field.options"
:key="option.id || option"

Timothee P
committed
class="ui checkbox"
>
<input
:id="option.id || option"

Timothee P
committed
type="checkbox"
:checked="field.value && field.value.includes(option.id || option)"
:name="option.id || option"

Timothee P
committed
@change="selectMultipleCheckbox"
>
<label :for="option.id || option">
{{ option.name || option }}

Timothee P
committed
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
</label>
</div>
</div>
<Multiselect
v-else-if="field && field.field_type === 'pre_recorded_list'"
v-model="selectedPrerecordedValue"
:options="selectedPrerecordedListValues[field.options[0]] || []"
:options-limit="10"
:allow-empty="!field.is_mandatory"
track-by="label"
label="label"
:reset-after="false"
select-label=""
selected-label=""
deselect-label=""
:searchable="true"
:placeholder="'Recherchez une valeur de la liste pré-définie ...'"
:show-no-results="true"
:loading="loadingPrerecordedListValues"
:clear-on-select="false"
:preserve-search="false"
@search-change="search"
@select="selectPrerecordedValue"
>
<template slot="clear">
<div
v-if="selectedPrerecordedValue"
class="multiselect__clear"
@click.prevent.stop="clearPrerecordedValue"
>
<i
class="close icon"
aria-hidden="true"
/>
</div>
</template>
<span slot="noResult">
Aucun résultat.
</span>
<span slot="noOptions">
Saisissez les premiers caractères ...
</span>
</Multiselect>
</template>
Sébastien DA ROCHA
committed
</div>
</template>
<script>
import { mapState, mapActions, mapMutations } from 'vuex';
import Multiselect from 'vue-multiselect';
import Dropdown from '@/components/Dropdown.vue';
Sébastien DA ROCHA
committed
export default {

Timothee P
committed
name: 'ExtraForm',
Sébastien DA ROCHA
committed
components: {
Dropdown,
Sébastien DA ROCHA
committed
},
props: {
field: {
type: Object,
default: null,
type: Boolean,
default: false,
Sébastien DA ROCHA
committed
prerecordedListSearchQuery: null,
loadingPrerecordedListValues: false,
selectedPrerecordedValue: null,
selectedMultipleCheckbox: [],
Sébastien DA ROCHA
committed
computed: {
...mapState('feature-type', [
'selectedPrerecordedListValues'
]),
...mapState('feature', [
'extra_forms',
]),
Sébastien DA ROCHA
committed
selected_extra_form_list: {
get() {
Sébastien DA ROCHA
committed
},
set(newValue) {
//* set the value selected in the dropdown
this.$emit('update:value', newValue.id || newValue);
} else {
const newExtraForm = this.field;
newExtraForm['value'] = newValue;
this.$store.commit('feature/UPDATE_EXTRA_FORM', newExtraForm);
}
Sébastien DA ROCHA
committed
},
},
displayLabels() {
return this.$route.name === 'editer-signalement' || this.$route.name === 'ajouter-signalement' || this.$route.name === 'editer-attribut-signalement';
Sébastien DA ROCHA
committed
},

Timothee P
committed
/**
* Watches for changes in the 'field.value' and updates the form state accordingly.
* This watcher handles specific field types, ensuring their values are correctly initialized
* and updated in scenarios like fast edition mode where certain values might not be auto-refreshed.
*
* @param {*} newValue - The new value of the field.
* @param {*} oldValue - The previous value of the field before the change.
*/
'field.value': function(newValue, oldValue) {

Timothee P
committed
// Check if the field object exists.

Timothee P
committed
// Handle pre-recorded list fields specifically.
if (this.field.field_type === 'pre_recorded_list') {

Timothee P
committed
// Update the form value if both new and old values are defined and different,
// or if either value is undefined, indicating a change.
if ((newValue && oldValue && (newValue.label !== oldValue.label || newValue !== oldValue))
|| !newValue || !oldValue) {
this.initPrerecordedXform(); // Reinitialize the field to reflect the updated value.
}
} else if (this.field.field_type === 'multi_choices_list') {

Timothee P
committed
// For multi-choice lists, reinitialize the field if the array values have changed.
// This is crucial in fast edition modes to prevent overriding the current value with a stale value.
this.initMultipleCheckboxXform();

Timothee P
committed
// Reset any error states for the field.
prerecordedListSearchQuery(newValue) {
this.loadingPrerecordedListValues = true;
this.GET_SELECTED_PRERECORDED_LIST_VALUES({
name: this.field.options[0],
pattern: newValue
})
.then(() => {
this.loadingPrerecordedListValues = false;
})
.catch(() => {
this.loadingPrerecordedListValues = false;
});
}
if (this.field) {
if (this.field.field_type === 'pre_recorded_list') {
this.initPrerecordedXform();
} else if (this.field.field_type === 'multi_choices_list') {
this.initMultipleCheckboxXform();

Timothee P
committed
// autoset field to false if is a boolean, since user doesn't need to select it, when false value is expected
if (this.field.field_type === 'boolean' && (this.field.value === undefined || this.field.value === null)) {
this.updateStore_extra_form(false);

Timothee P
committed
}
Sébastien DA ROCHA
committed
methods: {
...mapActions('feature-type', [
'GET_SELECTED_PRERECORDED_LIST_VALUES'
]),
...mapMutations('feature', [
'UPDATE_EXTRA_FORM',
'SET_EXTRA_FORMS',
initMultipleCheckboxXform() {
this.selectedMultipleCheckbox = typeof this.field.value === 'string' ? this.field.value.split(',') : this.field.value || [];
},
initPrerecordedXform() {
const { options, value } = this.field;
this.loadingPrerecordedListValues = true;
this.GET_SELECTED_PRERECORDED_LIST_VALUES({
name: options[0],
pattern: ''
})
.then(() => {
this.loadingPrerecordedListValues = false;
})
.catch(() => {
this.loadingPrerecordedListValues = false;
});
if (value) {
this.selectedPrerecordedValue = { label: value.label ? value.label : value };
} else {
this.selectedPrerecordedValue = null;
}
},

Timothee P
committed
/**
* Updates the Vuex store or component state with the new value for a form field.
* This function handles different types of form fields including boolean, multi-choice lists, and others.
*
* @param {Event|*} val - The new value or an event object.
*/
updateStore_extra_form(val) {
// Check if the field object is defined.

Timothee P
committed
// If the function is triggered by an event from a template input.
if (val && val.target) {
// For boolean fields (like checkboxes), use the 'checked' property.
if (this.field.field_type === 'boolean') {

Timothee P
committed
newValue = val.target.checked;

Timothee P
committed
// For other input types, use the 'value' property.
newValue = val.target.value;

Timothee P
committed
} else if (this.field.field_type === 'multi_choices_list') {
// For multi-choice lists, the value is stored in component state.
newValue = this.selectedMultipleCheckbox;

Timothee P
committed
} else {
// If the function is called directly with a value (not from an event).
newValue = val;

Timothee P
committed
// Set the new value for the field.

Timothee P
committed
// If the component is used to update directly a returned value, emit an event with the new value.

Timothee P
committed
// Otherwise, update the Vuex store with the new value for the extra form field.
this.UPDATE_EXTRA_FORM({ ...this.field, value: newValue });
Sébastien DA ROCHA
committed
},
if (this.field && this.field.is_mandatory && !this.field.value) {
isValid = false;
this.error = 'Ce champ est obligatoire';
} else {
this.error = null;
}
return isValid;
search(text) {
this.prerecordedListSearchQuery = text;
},
selectPrerecordedValue(e) {
this.prerecordedListSearchQuery = null;

Timothee P
committed
this.updateStore_extra_form({ target: { value: this.selectedPrerecordedValue.label } });
this.selectedPrerecordedValue = null;
this.prerecordedListSearchQuery = null;
this.updateStore_extra_form({ target: { value: null } });

Timothee P
committed
/**
* Handles the selection and deselection of checkboxes in a form.
* This function updates an array to track the selected checkboxes by their names.
* It's typically called on the change event of each checkbox.
*
* @param {Event} e - The event object from the checkbox input.
*/
selectMultipleCheckbox(e) {

Timothee P
committed
// Destructure the 'checked' status and 'name' of the checkbox from the event target.
const { checked, name } = e.target;

Timothee P
committed
// If the checkbox is checked, add its name to the array of selected checkboxes.
// Cloning the array to allow unsaved changes detection (it wasn't working with Array.push)
if (checked) {

Timothee P
committed
this.selectedMultipleCheckbox = [...this.selectedMultipleCheckbox, name];
} else {

Timothee P
committed
// If the checkbox is unchecked, remove its name from the array.
this.selectedMultipleCheckbox = this.selectedMultipleCheckbox.filter((el) => el !== name);
}

Timothee P
committed
// Call a method to update the Vuex store or component state with the latest selection.
this.updateStore_extra_form();
},

Timothee P
committed
Sébastien DA ROCHA
committed
},
};
label.required:after {
content: ' *';
color: rgb(209, 0, 0);
}
.checkbox_list {
display: flex;
flex-direction: column;
.ui.checkbox {
font-weight: normal;
}

Timothee P
committed
.multiselect__placeholder {
position: absolute;
width: calc(100% - 48px);
overflow: hidden;
text-overflow: ellipsis;

Timothee P
committed
.multiselect__tags {
position: relative;
}
/* keep font-weight from overide of semantic classes */
.multiselect__placeholder, .multiselect__content, .multiselect__tags {
font-weight: initial !important;
}
/* keep placeholder eigth */
.multiselect .multiselect__placeholder {
margin-bottom: 9px !important;
padding-top: 1px;
}
/* keep placeholder height when opening dropdown without selection */
input.multiselect__input {
padding: 3px 0 0 0 !important;
}
/* keep placeholder height when opening dropdown with already a value selected */
.multiselect__tags .multiselect__single {
padding: 1px 0 0 0 !important;
margin-bottom: 9px;
}