Moves TermsList to a separate component. Still needs to fix router watch inside child component.

This commit is contained in:
mateuswetah 2018-03-27 11:57:49 -03:00
parent 33d10c6d51
commit 88e83fe661
5 changed files with 370 additions and 343 deletions

View File

@ -103,67 +103,8 @@
<!-- Terms List -->
<b-field
:addons="false"
:label="$i18n.get('label_category_terms')">
<button
class="button is-secondary is-small"
type="button"
@click="addNewTerm()">
{{ $i18n.get('label_new_term') }}
</button>
<!-- Term item -->
<div
class="term-item"
:class="{
'not-sortable-item': term.term_id == 'new' || term.term_id == undefined || openedTermId != '' ,
'not-focusable-item': openedTermId == term.term_id
}"
v-for="(term, index) in termsList"
:key="index">
<span
class="term-name"
:class="{'is-danger': formWithErrors == term.term_id }">
{{ term.name }}
</span>
<span
v-if="term.term_id != undefined"
class="label-details">
<span
class="not-saved"
v-if="(editForms[term.term_id] != undefined && editForms[term.term_id].saved != true) || term.term_id == 'new'">
{{ $i18n.get('info_not_saved') }}
</span>
</span>
<span
class="loading-spinner"
v-if="term.term_id == undefined"/>
<span
class="controls"
v-if="term.term_id !== undefined || term.term_id !== 'new'">
<a @click.prevent="editTerm(term)">
<b-icon
type="is-gray"
icon="pencil"/>
</a>
<a @click.prevent="removeTerm(term)">
<b-icon
type="is-gray"
icon="delete"/>
</a>
</span>
<div v-if="openedTermId == term.term_id">
<term-edition-form
:category-id="categoryId"
@onEditionFinished="onTermEditionFinished()"
@onEditionCanceled="onTermEditionCanceled()"
@onErrorFound="formWithErrors = term.term_id"
:index="index"
:original-term="term"
:edited-term="editForms[term.term_id]"/>
</div>
</div>
:label="$i18n.get('label_category_terms')">
<terms-list :category-id="categoryId"/>
</b-field>
<!-- Submit -->
@ -195,7 +136,7 @@
<script>
import { wpAjax } from "../../js/mixins";
import { mapActions, mapGetters } from 'vuex';
import TermEditionForm from './term-edition-form.vue'
import TermsList from '../lists/terms-list.vue'
import htmlToJSON from 'html-to-json';
export default {
@ -228,24 +169,11 @@
label: this.$i18n.get('trash')
}],
editFormErrors: {},
formErrorMessage: '',
// Terms related
isLoadingTerms: false,
formWithErrors: '',
openedTermId: '',
editForms: [],
orderedTermsList: []
}
},
computed: {
termsList() {
//this.orderedTermsList = new Array();
//this.buildOrderedTermsList(0, 0);
return this.getTerms();
formErrorMessage: ''
}
},
components: {
TermEditionForm
TermsList
},
methods: {
...mapActions('category', [
@ -253,13 +181,9 @@
'updateCategory',
'fetchCategory',
'fetchOnlySlug',
'fetchTerms',
'updateTerm',
'deleteTerm'
]),
...mapGetters('category',[
'getCategory',
'getTerms'
]),
onSubmit() {
@ -379,8 +303,7 @@
this.isLoadingCategory = false;
}
)
})
.catch(error => this.$console.error(error));
},
clearErrors(attribute) {
@ -391,73 +314,7 @@
},
labelNewTerms(){
return ( this.form.allowInsert === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
},
addNewTerm() {
let newTerm = {
categoryId: this.categoryId,
name: '',
description: '',
parent: 0,
term_id: 'new'
}
this.termsList.push(newTerm);
this.editTerm(newTerm);
},
editTerm(term) {
// Closing collapse
if (this.openedTermId == term.term_id) {
this.openedTermId = '';
// Opening collapse
} else {
this.openedTermId = term.term_id;
// First time opening
if (this.editForms[this.openedTermId] == undefined) {
this.editForms[this.openedTermId] = JSON.parse(JSON.stringify(term));
this.editForms[this.openedTermId].saved = true;
if (term.term_id == 'new')
this.editForms[this.openedTermId].saved = false;
}
}
},
removeTerm(term) {
this.$console.log(term);
this.deleteTerm({categoryId: this.categoryId, termId: term.term_id})
.then(() => {
})
.catch((error) => {
this.$console.log(error);
});
},
onTermEditionFinished() {
this.formWithErrors = '';
delete this.editForms[this.openedTermId];
this.openedTermId = '';
},
onTermEditionCanceled() {
this.formWithErrors = '';
delete this.editForms[this.openedTermId];
this.openedTermId = '';
},
buildOrderedTermsList(parentId, termDepth) {
for (let term of this.termsList) {
if (term['parent'] != parentId ) {
continue;
}
term.depth = termDepth;
this.orderedTermsList.push(term);
this.buildOrderedTermsList(term.term_id, termDepth + 1);
}
},
}
},
created(){
@ -466,7 +323,6 @@
} else if (this.$route.fullPath.split("/").pop() === "edit") {
this.isLoadingCategory = true;
this.isLoadingTerms = true;
// Obtains current category ID from URL
this.pathArray = this.$route.fullPath.split("/").reverse();
@ -484,45 +340,13 @@
this.isLoadingCategory = false;
});
this.fetchTerms(this.categoryId)
.then(() => {
// Fill this.form data with current data.
this.isLoadingCategory = false;
})
.catch((error) => {
this.$console.log(error);
});
}
},
beforeRouteLeave ( to, from, next ) {
let hasUnsavedForms = false;
for (let editForm in this.editForms) {
if (!this.editForms[editForm].saved)
hasUnsavedForms = true;
}
if ((this.openedTermId != '' && this.openedTermId != undefined) || hasUnsavedForms ) {
this.$dialog.confirm({
message: this.$i18n.get('info_warning_terms_not_saved'),
onConfirm: () => {
this.onEditionCanceled();
next();
},
cancelText: this.$i18n.get('cancel'),
confirmText: this.$i18n.get('continue'),
type: 'is-secondary'
});
} else {
next()
}
}
}
</script>
<style lang="scss" scoped>
@import "../../scss/_variables.scss";
.thumbnail-field {
width: 128px;
height: 128px;
@ -553,102 +377,6 @@
}
}
.loading-spinner {
animation: spinAround 500ms infinite linear;
border: 2px solid #dbdbdb;
border-radius: 290486px;
border-right-color: transparent;
border-top-color: transparent;
content: "";
display: inline-block;
height: 1em;
width: 1em;
}
.term-item {
background-color: white;
padding: 0.7em 0.9em;
margin: 4px;
min-height: 40px;
display: block;
position: relative;
cursor: grab;
.handle {
padding-right: 6em;
}
.grip-icon {
fill: $gray;
top: 2px;
position: relative;
}
.term-name {
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
font-weight: bold;
margin-left: 0.4em;
margin-right: 0.4em;
&.is-danger {
color: $danger !important;
}
}
.label-details {
font-weight: normal;
color: $gray;
}
.not-saved {
font-style: italic;
font-weight: bold;
color: $danger;
}
.controls {
position: absolute;
right: 5px;
top: 10px;
.icon {
bottom: 1px;
position: relative;
i, i:before { font-size: 20px; }
}
}
&.not-sortable-item, &.not-sortable-item:hover {
cursor: default;
background-color: white !important;
.handle .label-details, .handle .icon {
color: $gray !important;
}
}
&.not-focusable-item, &.not-focusable-item:hover {
cursor: default;
.term-name {
color: $primary;
}
.handle .label-details, .handle .icon {
color: $gray !important;
}
}
}
.term-item:hover:not(.not-sortable-item) {
background-color: $secondary;
border-color: $secondary;
color: white !important;
.label-details, .icon, .not-saved {
color: white !important;
}
.grip-icon {
fill: white;
}
}
</style>

View File

@ -6,27 +6,23 @@
<b-field
:addons="false"
:type="formErrors['name'] != undefined ? 'is-danger' : ''"
:message="formErrors['name'] != undefined ? formErrors['name'] : ''">
:type="editForm.name == '' ? 'is-danger' : ''"
:message="editForm.name == '' ? $i18n.get('info_name_is_required') : ''">
<label class="label">
{{ $i18n.get('label_name') }}
<span
class="required-term-asterisk"
:class="formErrors['name'] != undefined ? 'is-danger' : ''">*</span>
:class="editForm.name == '' ? 'is-danger' : ''">*</span>
<help-button
:title="$i18n.getHelperTitle('terms', 'name')"
:message="$i18n.getHelperMessage('terms', 'name')"/>
</label>
<b-input
v-model="editForm.name"
name="name"
@focus="clearErrors('name')"/>
name="name"/>
</b-field>
<b-field
:addons="false"
:type="formErrors['description'] != undefined ? 'is-danger' : ''"
:message="formErrors['description'] != undefined ? formErrors['description'] : ''">
<b-field :addons="false">
<label class="label">
{{ $i18n.get('label_description') }}
<help-button
@ -36,14 +32,10 @@
<b-input
type="textarea"
name="description"
v-model="editForm.description"
@focus="clearErrors('description')" />
v-model="editForm.description"/>
</b-field>
<b-field
:addons="false"
:type="formErrors['parent_term'] != undefined ? 'is-danger' : ''"
:message="formErrors['parent_term'] != undefined ? formErrors['parent_term'] : ''">
<b-field :addons="false">
<label class="label">
{{ $i18n.get('label_parent_term') }}
<help-button
@ -51,16 +43,17 @@
:message="$i18n.getHelperMessage('terms', 'parent_term')"/>
</label>
<b-select
id="parent_term_select"
v-model="editForm.parent"
:placeholder="$i18n.get('instruction_select_a_parent_term')">
id="parent_term_select"
v-model="editForm.parent"
:class="{'is-empty': editForm.parent == 0}"
:placeholder="$i18n.get('instruction_select_a_parent_term')">
<option
@focus="clearErrors('label_parent_term')"
id="tainacan-select-parent-term"
v-for="(parentTerm, index) in parentTermsList"
v-if="editForm.id != parentTerm.id"
:key="index"
:value="editForm.parent.id">
{{ parentTerm.name }}
:value="editForm.parent">
{{ parentTerm.name == 0 ? $i18n.get('instruction_select_a_parent_term') : parentTerm.name }}
</option>
</b-select>
</b-field>
@ -77,12 +70,12 @@
<div class="control">
<button
class="button is-success"
type="submit">
type="submit"
:disabled="editForm.name == '' || editForm.name == undefined">
{{ $i18n.get('save') }}
</button>
</div>
</div>
<p class="help is-danger">{{ formErrorMessage }}</p>
</form>
</template>
@ -95,8 +88,6 @@ export default {
return {
editForm: {},
oldForm: {},
formErrors: {},
formErrorMessage: '',
closedByForm: false
}
},
@ -113,10 +104,7 @@ export default {
},
created() {
this.editForm = this.editedTerm;
this.formErrors = this.editForm.formErrors != undefined ? this.editForm.formErrors : {};
this.formErrorMessage = this.editForm.formErrors != undefined ? this.editForm.formErrorMessage : '';
this.editForm = this.editedTerm;
this.oldForm = JSON.parse(JSON.stringify(this.originalTerm));
},
@ -141,59 +129,46 @@ export default {
]),
saveEdition(term) {
if (term.term_id == 'new') {
if (term.id == 'new') {
this.sendTerm({
categoryId: this.categoryId,
index: this.index,
name: this.editForm.name,
description: this.editForm.description,
parent: this.editForm.parent
categoryId: this.categoryId,
index: this.index,
name: this.editForm.name,
description: this.editForm.description,
parent: this.editForm.parent
})
.then(() => {
this.editForm = {};
this.formErrors = {};
this.formErrorMessage = '';
this.closedByForm = true;
this.$console.log("Tudo OK AQUI.");
this.$emit('onEditionFinished');
})
.catch((error) => {
// for (let error of errors.errors) {
// for (let attribute of Object.keys(error))
// this.formErrors[attribute] = error[attribute];
// }
// this.formErrorMessage = errors.error_message;
this.$emit('onErrorFound');
this.$console.log(error);
// this.editForm.formErrors = this.formErrors;
// this.editForm.formErrorMessage = this.formErrorMessage;
});
} else {
this.updateTerm({categoryId: this.categoryId, termId: term.term_id, index: this.index, options: this.editForm})
this.updateTerm({
categoryId: this.categoryId,
termId: term.id,
index: this.index,
name: this.editForm.name,
description: this.editForm.description,
parent: this.editForm.parent
})
.then(() => {
this.editForm = {};
this.formErrors = {};
this.formErrorMessage = '';
this.closedByForm = true;
this.$emit('onEditionFinished');
})
.catch((error) => {
// for (let error of errors.errors) {
// for (let attribute of Object.keys(error))
// this.formErrors[attribute] = error[attribute];
// }
// this.formErrorMessage = errors.error_message;
this.$emit('onErrorFound');
this.$console.log(error);
// this.editForm.formErrors = this.formErrors;
// this.editForm.formErrorMessage = this.formErrorMessage;
});
}
},
clearErrors(attribute) {
this.formErrors[attribute] = undefined;
},
cancelEdition() {
this.closedByForm = true;
this.$emit('onEditionCanceled');

View File

@ -0,0 +1,321 @@
<template>
<div>
<button
class="button is-secondary is-small"
type="button"
@click="addNewTerm()">
{{ $i18n.get('label_new_term') }}
</button>
<div
class="term-item"
:class="{
'not-sortable-item': term.id == 'new' || term.id == undefined || openedTermId != '' ,
'not-focusable-item': openedTermId == term.id
}"
v-for="(term, index) in termsList"
:key="index">
<span
class="term-name"
:class="{'is-danger': formWithErrors == term.id }">
{{ term.name }}
</span>
<span
v-if="term.id != undefined"
class="label-details">
<span
class="not-saved"
v-if="(editForms[term.id] != undefined && editForms[term.id].saved != true) || term.id == 'new'">
{{ $i18n.get('info_not_saved') }}
</span>
</span>
<span
class="loading-spinner"
v-if="term.id == undefined"/>
<span class="controls" >
<a @click.prevent="editTerm(term)">
<b-icon
type="is-gray"
icon="pencil"/>
</a>
<a
@click.prevent="removeTerm(term)">
<b-icon
type="is-gray"
icon="delete"/>
</a>
</span>
<div v-if="openedTermId == term.id">
<term-edition-form
:category-id="categoryId"
@onEditionFinished="onTermEditionFinished()"
@onEditionCanceled="onTermEditionCanceled()"
@onErrorFound="formWithErrors = term.id"
:index="index"
:original-term="term"
:edited-term="editForms[term.id]"/>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
import TermEditionForm from '../edition/term-edition-form.vue'
export default {
name: 'TermsList',
data(){
return {
// Terms related
isLoadingTerms: false,
formWithErrors: '',
openedTermId: '',
editForms: [],
orderedTermsList: []
}
},
props: {
categoryId: Number
},
computed: {
termsList() {
//this.orderedTermsList = new Array();
//this.buildOrderedTermsList(0, 0);
return this.getTerms();
}
},
components: {
TermEditionForm
},
beforeRouteUpdate ( to, from, next ) {
let hasUnsavedForms = false;
for (let editForm in this.editForms) {
if (!this.editForms[editForm].saved)
hasUnsavedForms = true;
}
if ((this.openedTermId != '' && this.openedTermId != undefined) || hasUnsavedForms ) {
this.$dialog.confirm({
message: this.$i18n.get('info_warning_terms_not_saved'),
onConfirm: () => {
this.onEditionCanceled();
next();
},
cancelText: this.$i18n.get('cancel'),
confirmText: this.$i18n.get('continue'),
type: 'is-secondary'
});
} else {
next()
}
},
methods: {
...mapActions('category', [
'fetchTerms',
'updateTerm',
'deleteTerm'
]),
...mapGetters('category',[
'getTerms'
]),
addNewTerm() {
let newTerm = {
categoryId: this.categoryId,
name: '',
description: '',
parent: 0,
id: 'new'
}
this.termsList.push(newTerm);
this.editTerm(newTerm);
},
editTerm(term) {
// Closing collapse
if (this.openedTermId == term.id) {
this.openedTermId = '';
// Opening collapse
} else {
this.openedTermId = term.id;
// First time opening
if (this.editForms[this.openedTermId] == undefined) {
this.editForms[this.openedTermId] = JSON.parse(JSON.stringify(term));
this.editForms[this.openedTermId].saved = true;
if (term.id == 'new')
this.editForms[this.openedTermId].saved = false;
}
}
},
removeTerm(term) {
if (term.id == 'new') {
let index = this.termsList.findIndex(deletedTerm => deletedTerm.id == 'new');
if (index >= 0) {
this.termsList.splice(index, 1);
}
if (this.openedTermId == 'new')
this.openedTermId = '';
delete this.editForms['new'];
} else {
this.deleteTerm({categoryId: this.categoryId, termId: term.id})
.then(() => {
})
.catch((error) => {
this.$console.log(error);
});
}
},
onTermEditionFinished() {
let index = this.termsList.findIndex(deletedTerm => deletedTerm.id == 'new');
if (index >= 0) {
this.termsList.splice(index, 1);
}
this.formWithErrors = '';
delete this.editForms[this.openedTermId];
this.openedTermId = '';
},
onTermEditionCanceled() {
this.formWithErrors = '';
delete this.editForms[this.openedTermId];
this.openedTermId = '';
},
buildOrderedTermsList(parentId, termDepth) {
for (let term of this.termsList) {
if (term['parent'] != parentId ) {
continue;
}
term.depth = termDepth;
this.orderedTermsList.push(term);
this.buildOrderedTermsList(term.id, termDepth + 1);
}
},
},
created() {
this.isLoadingTerms = true;
this.fetchTerms(this.categoryId)
.then(() => {
// Fill this.form data with current data.
this.isLoadingTerms = false;
})
.catch((error) => {
this.$console.log(error);
});
}
}
</script>
<style lang="scss">
@import "../../scss/_variables.scss";
.loading-spinner {
animation: spinAround 500ms infinite linear;
border: 2px solid #dbdbdb;
border-radius: 290486px;
border-right-color: transparent;
border-top-color: transparent;
content: "";
display: inline-block;
height: 1em;
width: 1em;
}
.term-item {
font-size: 14px;
background-color: white;
padding: 0.7em 0.9em;
margin: 4px;
min-height: 40px;
display: block;
position: relative;
cursor: grab;
.handle {
padding-right: 6em;
}
.grip-icon {
fill: $gray;
top: 2px;
position: relative;
}
.term-name {
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
font-weight: bold;
margin-left: 0.4em;
margin-right: 0.4em;
&.is-danger {
color: $danger !important;
}
}
.label-details {
font-weight: normal;
color: $gray;
}
.not-saved {
font-style: italic;
font-weight: bold;
color: $danger;
}
.controls {
position: absolute;
right: 5px;
top: 10px;
.icon {
bottom: 1px;
position: relative;
i, i:before { font-size: 20px; }
}
}
&.not-sortable-item, &.not-sortable-item:hover {
cursor: default;
background-color: white !important;
.handle .label-details, .handle .icon {
color: $gray !important;
}
}
&.not-focusable-item, &.not-focusable-item:hover {
cursor: default;
.term-name {
color: $primary;
}
.handle .label-details, .handle .icon {
color: $gray !important;
}
}
}
.term-item:hover:not(.not-sortable-item) {
background-color: $secondary;
border-color: $secondary;
color: white !important;
.label-details, .icon, .not-saved {
color: white !important;
}
.grip-icon {
fill: white;
}
}
</style>

View File

@ -142,7 +142,7 @@ export const deleteTerm = ({ commit }, { categoryId, termId }) => {
axios.tainacan.delete(`/taxonomy/${categoryId}/terms/${termId}?permanently=${true}`)
.then(res => {
let term = res.data;
commit('deleteTerm', term);
commit('deleteTerm', termId);
resolve( term );
})
.catch(error => {
@ -151,9 +151,13 @@ export const deleteTerm = ({ commit }, { categoryId, termId }) => {
});
};
export const updateTerm = ({ commit }, { categoryId, termId, index, options }) => {
export const updateTerm = ({ commit }, { categoryId, termId, index, name, description, parent }) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.patch('/taxonomy/' + categoryId + '/terms/' + termId, options)
axios.tainacan.patch('/taxonomy/' + categoryId + '/terms/' + termId, {
name: name,
description: description,
parent: parent
})
.then( res => {
let term = res.data;
console.log(term);
@ -170,7 +174,7 @@ export const updateTerm = ({ commit }, { categoryId, termId, index, options }) =
export const fetchTerms = ({ commit }, categoryId ) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/taxonomy/${categoryId}/terms/`)
axios.tainacan.get(`/taxonomy/${categoryId}/terms/?hideempty=0`)
.then(res => {
let terms = res.data;
commit('setTerms', terms);

View File

@ -34,9 +34,8 @@ export const setTerms = (state, terms) => {
state.terms = terms;
};
export const deleteTerm = ( state, term ) => {
let index = state.terms.findIndex(deletedTerm => deletedTerm.id === term.id);
export const deleteTerm = ( state, termId ) => {
let index = state.terms.findIndex(deletedTerm => deletedTerm.id === termId);
if (index >= 0) {
state.terms.splice(index, 1);
}