Bug fixes and usability improvements on Add New Term component. Closes #129.

This commit is contained in:
Mateus Machado Luna 2018-09-14 10:10:41 -03:00
parent 74130c6bc0
commit 8f24756fd1
6 changed files with 201 additions and 58 deletions

View File

@ -936,7 +936,7 @@ export default {
position: absolute;
z-index: 99;
right: 0;
top: 70px;
top: 148px;
max-width: 36px;
height: 36px;
width: 36px;

View File

@ -54,20 +54,27 @@
}
.taginput-container {
padding: 0 !important;
padding: 0px !important;
background-color: white !important;
&:focus, &:active {
border: none !important;
}
.autocomplete .icon {
height: 2.2em !important;
}
.input {
margin-bottom: 0px !important;
height: 1.85rem !important;
text-overflow: ellipsis;
}
.input.has-selected, .input:focus, .input:active {
background-color: white;
border: 1px solid $gray2 !important;
}
.tags {
margin: 0.17rem 0.25rem 0.08rem 0.25rem !important;
}
.tag {
background: white;
padding-right: 0;
@ -100,7 +107,7 @@
font-size: 0.75rem;
.tags {
margin-right: 8px;
margin: 4px 6px 0px 6px;
}
.tag {
background: white;

View File

@ -191,7 +191,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'label_collection_filters' => __( 'Collection Filters', 'tainacan' ),
'label_parent_term' => __( 'Parent Term', 'tainacan' ),
'label_children_terms' => __( 'children terms', 'tainacan' ),
'label_new_term' => __( 'New Term', 'tainacan' ),
'label_new_term' => __( 'Create New Term', 'tainacan' ),
'label_new_child' => __( 'New Child', 'tainacan' ),
'label_taxonomy_terms' => __( 'Taxonomy Terms', 'tainacan' ),
'label_no_parent_term' => __( 'No parent term', 'tainacan' ),
@ -339,6 +339,7 @@ return apply_filters( 'tainacan-admin-i18n', [
'instruction_select_collection_fetch_items' => __( 'Select a collection to fecth items', 'tainacan' ),
'instruction_select_a_action' => __( 'Select a action', 'tainacan' ),
'instruction_parent_term' => __( 'Type to search a Parent Term to choose.', 'tainacan' ),
'instruction_type_existing_term' => __( 'Type to add an existing term...', 'tainacan' ),
// Info. Other feedback to user.
'info_search_results' => __( 'Search Results', 'tainacan' ),

View File

@ -1,8 +1,8 @@
<template>
<div>
<span>
<span v-if="!showForm">
<a
@click="showForm = !showForm"
@click="toggleForm()"
class="is-inline add-link">
<b-icon
icon="plus-circle"
@ -10,54 +10,121 @@
type="is-secondary"/>
&nbsp;{{ $i18n.get('label_new_term') }}</a>
</span>
<div>
<!-- <transition name="fade"> -->
</span>
<transition name="appear">
<section
v-if="showForm"
style="padding-left: 0px; margin-top: 12px; margin-bottom: -12px;">
<b-field
:addons="false"
:type="((formErrors.name !== '' || formErrors.repeated !== '') && (formErrors.name !== undefined || formErrors.repeated !== undefined )) ? 'is-danger' : ''"
:message="formErrors.name != undefined? formErrors : formErrors.repeated">
<label class="label is-inline">
{{ $i18n.get('label_name') }}
<span class="required-term-asterisk">*</span>
<help-button
:title="$i18n.get('label_name')"
:message="$i18n.get('info_help_term_name')"/>
</label>
<b-input
:class="{'has-content': name != undefined && name != ''}"
v-model="name"
@focus="clearErrors({ name: 'name', repeated: 'repeated' })"/>
</b-field>
<section
v-if="showForm"
style="padding-left: 0px;">
<!-- <b-field :label="$i18n.get('label_parent_term')">
<b-select
v-model="parent">
<option
:value="0"
selected> ---{{ $i18n.get('label_parent_term') }}--- </option>
<option
v-for="(option,index) in options"
:key="index"
:value="option.id"
v-html="setSpaces( option.level ) + option.name"/>
</b-select>
</b-field> -->
<b-field :label="$i18n.get('label_name')">
<b-input
:class="{'has-content': name != undefined && name != ''}"
v-model="name"/>
</b-field>
<!-- Parent -------------- -->
<b-field
:addons="false"
:type="((formErrors.parent !== '' || formErrors.repeated !== '') && (formErrors.parent !== undefined || formErrors.repeated !== undefined )) ? 'is-danger' : ''"
:message="formErrors.parent ? formErrors : formErrors.repeated">
<label class="label is-inline">
{{ $i18n.get('label_parent_term') }}
<b-switch
@input="onToggleSwitch()"
id="tainacan-checkbox-has-parent"
size="is-small"
v-model="hasParent" />
<help-button
:title="$i18n.get('label_parent_term')"
:message="$i18n.get('info_help_parent_term')"/>
</label>
<b-autocomplete
id="tainacan-text-cover-page"
:placeholder="$i18n.get('instruction_parent_term')"
:data="parentTerms"
field="name"
v-model="parentTermName"
@select="onSelectParentTerm($event)"
:loading="isFetchingParentTerms"
@input="fecthParentTerms($event)"
@focus="clearErrors('parent');"
:disabled="!hasParent">
<template slot-scope="props">
{{ props.option.name }}
</template>
<template slot="empty">{{ $i18n.get('info_no_parent_term_found') }}</template>
</b-autocomplete>
<transition name="fade">
<p
class="checkboxes-warning"
v-show="showCheckboxesWarning == true">
{{ $i18n.get('info_warning_changing_parent_term') }}
</p>
</transition>
</b-field>
<b-field :label="$i18n.get('label_parent_term')">
<b-select
v-model="parent">
<option
:value="0"
selected> ---{{ $i18n.get('label_parent_term') }}--- </option>
<option
v-for="(option,index) in options"
:key="index"
:value="option.id"
v-html="setSpaces( option.level ) + option.name"/>
</b-select>
</b-field>
<button
:class="{ 'is-loading': isAddingNewTerm }"
class="button is-outlined"
@click="toggleForm()"
type="button">
{{ $i18n.get('cancel') }}
</button>
<a
class="button is-secondary"
@click="save">{{ $i18n.get('save') }}</a>
</section>
<button
:class="{ 'is-loading': isAddingNewTerm }"
class="button is-secondary"
@click="save"
type="button">
{{ $i18n.get('save') }}
</button>
</section>
<!-- </transition> -->
</div>
</transition>
</div>
</template>
<script>
import { tainacan as axios } from '../../../js/axios/axios'
import { mapActions } from 'vuex';
export default {
data(){
return {
name: '',
parent: 0,
hasParent: false,
showForm: false,
metadatum_id: this.metadatum.metadatum.id
parentTerms: [],
search: '',
parentTermName: '',
isAddingNewTerm: false,
isFetchingParentTerms: false,
metadatum_id: this.metadatum.metadatum.id,
formErrors: {}
}
},
props: {
@ -68,9 +135,25 @@
value:[ Array, Boolean, Number ],
options: {
type: Array
}
},
componentType: ''
},
methods: {
...mapActions('taxonomy', [
'fetchPossibleParentTerms'
]),
toggleForm() {
this.name = '';
this.parent = 0;
this.hasParent = false;
this.parentTerms = [];
this.search = '';
this.parentTermName = '';
this.isFetchingParentTerms = false;
this.isAddingNewTerm = false;
this.formErrors = {};
this.showForm = !this.showForm;
},
setSpaces( level ){
let result = '';
let space = '&nbsp;&nbsp;'
@ -80,6 +163,39 @@
return result;
},
fecthParentTerms(search) {
this.isFetchingParentTerms = true;
this.fetchPossibleParentTerms({
taxonomyId: this.taxonomy_id,
termId: 'new',
search: search })
.then((parentTerms) => {
this.parentTerms = parentTerms;
this.isFetchingParentTerms = false;
})
.catch((error) => {
this.$console.error(error);
this.isFetchingParentTerms = false;
});
},
onToggleSwitch() {
this.clearErrors('parent');
},
onSelectParentTerm(selectedParentTerm) {
this.parent = selectedParentTerm.id;
this.selectedParentTerm = selectedParentTerm;
this.parentTermName = selectedParentTerm.name;
},
clearErrors(attributes) {
if(attributes instanceof Object){
for(let attribute in attributes){
this.formErrors[attribute] = undefined;
}
} else {
this.formErrors[attributes] = undefined;
}
},
save(){
if( this.name.trim() === ''){
this.$toast.open({
@ -89,15 +205,15 @@
type: 'is-danger'
})
} else {
const instance = this;
this.isAddingNewTerm = true;
axios.post(`/taxonomy/${this.taxonomy_id}/terms?hideempty=0&order=asc`, {
name: this.name,
parent: this.parent
})
.then( res => {
instance.name = '';
instance.parent = 0;
this.isAddingNewTerm = false;
if( res.data && res.data.id || res.id ){
let id = ( res.id ) ? res.id : res.data.id;
@ -107,22 +223,37 @@
axios.patch(`/item/${this.item_id}/metadata/${this.metadatum_id}`, {
values: id,
}).then(() => {
instance.$emit('newTerm', id);
this.$emit('newTerm', { values: id, taxonomyId: this.taxonomy_id, metadatumId: this.metadatum_id });
this.toggleForm();
})
} else {
val = ( val ) ? val : [];
val.push( id );
val.push( this.componentType == ('tainacan-taxonomy-checkbox' || 'tainacan-taxonomy-radio') ? id : {'label': this.name, 'value': id} );
axios.patch(`/item/${this.item_id}/metadata/${this.metadatum_id}`, {
values: val,
}).then( () => {
instance.$emit('newTerm', val);
}).then(() => {
this.$emit('newTerm', { values: val, taxonomyId: this.taxonomy_id, metadatumId: this.metadatum_id });
this.toggleForm();
})
}
}
})
.catch((error) => {
let errors = { error_message: error['response']['data'].error_message, errors: error['response']['data'].errors };
for (let error of errors.errors) {
for (let metadatum of Object.keys(error)) {
this.$set(this.formErrors, metadatum, (this.formErrors[metadatum] !== undefined ? this.formErrors[metadatum] : '') + error[metadatum] + '\n');
}
}
this.isAddingNewTerm = false;
});
}
}
},
mounted() {
this.hasParent = this.parent != undefined && this.parent > 0;
}
}
</script>

View File

@ -18,7 +18,8 @@
</a>
<add-new-term
class="add-new-term"
v-if="getComponent !== 'tainacan-taxonomy-tag-input' && allowNew"
v-if="allowNew"
:component-type="getComponent"
:taxonomy_id="taxonomy"
:metadatum="metadatum"
:item_id="metadatum.item.id"
@ -89,7 +90,7 @@
componentAttribute: {
type: String
},
value: [ Number, String, Array,Object ],
value: [ Number, String, Array, Object ],
id: '',
disabled: false,
forcedComponentType: '',
@ -171,12 +172,14 @@
this.$emit('input', this.inputValue);
this.$emit('blur');
},
reload( val ){
this.valueComponent = val;
this.terms = [];
this.getTermsFromTaxonomy();
this.getTermsId();
reload( $event ) {
if ($event.taxonomyId == this.taxonomy && $event.metadatumId == this.metadatum.metadatum.id) {
this.valueComponent = $event.values;
this.terms = [];
this.offset = 0;
this.getTermsFromTaxonomy();
this.getTermsId();
}
}
}
}

View File

@ -4,7 +4,7 @@
:disabled="disabled"
size="is-small"
icon="magnify"
:allow-new="allowNew"
:allow-new="false"
:maxtags="maxtags"
@add="emitAdd"
@remove="emitRemove"
@ -13,14 +13,15 @@
field="label"
attached
ellipsis
:placeholder="$i18n.get('instruction_type_existing_term')"
:loading="isFetching"
:class="{'has-selected': selected != undefined && selected != []}"
autocomplete
@typing="autoCompleteTerm"/>
</div>
</template>
<script>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
@ -110,7 +111,7 @@
let val = this.selected;
let results = [];
if(val.length > 0){
if (val.length > 0){
for( let term of val ){
results.push( term.value );
}