Category creation page, Category edition page and init of category page

This commit is contained in:
weryques 2018-03-22 15:30:57 -03:00
parent 36571e34bf
commit 780f5a998d
14 changed files with 531 additions and 21 deletions

View File

@ -1,22 +1,352 @@
<template> <template>
<div> <div>
<div class="page-container"> <div class="page-container primary-page">
<h1>Category Edition Form</h1> <b-tag v-if="category != null && category != undefined" :type="'is-' + getStatusColor(category.status)" v-text="category.status"></b-tag>
<form v-if="category != null && category != undefined" class="tainacan-form" label-width="120px">
<!-- Name -------------------------------- -->
<b-field
:label="$i18n.get('label_name')"
:type="editFormErrors['name'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['name'] != undefined ? editFormErrors['name'] : ''">
<b-input
id="tainacan-text-name"
v-model="form.name"
@focus="clearErrors('name')"
@blur="updateSlug()">
</b-input>
</b-field>
<!-- Description -------------------------------- -->
<b-field
:label="$i18n.get('label_description')"
:type="editFormErrors['description'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['description'] != undefined ? editFormErrors['description'] : ''">
<b-input
id="tainacan-text-description"
type="textarea"
v-model="form.description"
@focus="clearErrors('description')">
</b-input>
</b-field>
<!-- Status -------------------------------- -->
<b-field
:label="$i18n.get('label_status')"
:type="editFormErrors['status'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['status'] != undefined ? editFormErrors['status'] : ''">
<b-select
id="tainacan-select-status"
v-model="form.status"
@focus="clearErrors('status')"
:placeholder="$i18n.get('instruction_select_a_status')">
<option
v-for="statusOption in statusOptions"
:key="statusOption.value"
:value="statusOption.value"
:disabled="statusOption.disabled">{{ statusOption.label }}
</option>
</b-select>
</b-field>
<!-- Slug -------------------------------- -->
<b-field
:label="$i18n.get('label_slug')"
:type="editFormErrors['slug'] != undefined ? 'is-danger' : ''"
:message="editFormErrors['slug'] != undefined ? editFormErrors['slug'] : ''">
<b-input
id="tainacan-text-slug"
v-model="form.slug"
@focus="clearErrors('slug')"
:disabled="isUpdatingSlug">
</b-input>
</b-field>
<!-- Allow Insert -->
<b-field :addons="false">
<label class="label">
{{ $i18n.get('label_category_allow_new_terms') }}
<a class="help-button"><b-icon size="is-small" icon="help-circle-outline"></b-icon></a>
</label>
<div class="block" >
<b-checkbox
v-model="form.allowInsert"
true-value="yes"
false-value="no">
{{ labelNewTerms() }}
</b-checkbox>
</div>
</b-field>
<button
id="button-cancel-category-creation"
class="button"
type="button"
@click="cancelBack">{{ $i18n.get('cancel') }}</button>
<button
id="button-submit-category-creation"
@click.prevent="onSubmit"
class="button is-primary">{{ $i18n.get('save') }}</button>
<p class="help is-danger">{{formErrorMessage}}</p>
</form>
<b-loading :active.sync="isLoading" :canCancel="false"></b-loading>
</div> </div>
</div> </div>
</template> </template>
<script> <script>
import { mapActions, mapGetters } from 'vuex'
export default { export default {
name: 'CategoryEditionForm', name: 'CategoryEditionForm',
data(){ data(){
return { return {
categoryId: Number,
category: null,
isLoading: false,
isUpdatingSlug: false,
form: {
name: String,
status: String,
description: String,
slug: String,
allowInsert: String,
},
statusOptions: [{
value: 'publish',
label: this.$i18n.get('publish')
}, {
value: 'draft',
label: this.$i18n.get('draft')
}, {
value: 'private',
label: this.$i18n.get('private')
}, {
value: 'trash',
label: this.$i18n.get('trash')
}],
editFormErrors: {},
formErrorMessage: '',
}
},
methods: {
...mapActions('category', [
'createCategory',
'updateCategory',
'fetchCategory',
'fetchOnlySlug',
]),
...mapGetters('category',[
'getCategory'
]),
onSubmit() {
this.isLoading = true;
let data = {
categoryId: this.categoryId,
name: this.form.name,
description: this.form.description,
slug: this.form.slug,
status: this.form.status,
allowInsert: this.form.allowInsert
};
this.updateCategory(data)
.then(updatedCategory => {
this.category = updatedCategory;
// Fill this.form data with current data.
this.form.name = this.category.name;
this.form.slug = this.category.slug;
this.form.description = this.category.description;
this.form.status = this.category.status;
this.allowInsert = this.category.allow_insert;
this.isLoading = false;
this.formErrorMessage = '';
this.editFormErrors = {};
this.$router.push(this.$routerHelper.getCategoryPath(this.categoryId));
})
.catch((errors) => {
for (let error of errors.errors) {
for (let attribute of Object.keys(error)) {
this.editFormErrors[attribute] = error[attribute];
}
}
this.formErrorMessage = errors.error_message;
this.isLoading = false;
});
},
updateSlug(){
if(!this.form.name || this.form.name.length <= 0){
return;
}
this.isUpdatingSlug = true;
let data = {
categoryId: this.categoryId,
name: this.form.name,
description: this.form.description,
//slug: '',
status: 'private',
allowInsert: this.form.allowInsert
};
console.log(data);
this.updateCategory(data)
.then(updatedCategory => {
this.category = updatedCategory;
console.info(this.category);
// Fill this.form data with current data.
this.form.name = this.category.name;
this.form.slug = this.category.slug;
this.form.description = this.category.description;
this.form.status = this.category.status;
this.allowInsert = this.category.allow_insert;
this.isUpdatingSlug = false;
this.formErrorMessage = '';
this.editFormErrors = {};
})
.catch(errors => {
for (let error of errors.errors) {
for (let attribute of Object.keys(error)) {
this.editFormErrors[attribute] = error[attribute];
}
}
this.formErrorMessage = errors.error_message;
this.isLoading = false;
});
},
getStatusColor(status) {
switch(status) {
case 'publish':
return 'success';
case 'draft':
return 'info';
case 'private':
return 'warning';
case 'trash':
return 'danger';
default:
return 'info';
}
},
createNewCategory() {
// Puts loading on Draft Category creation
this.isLoading = true;
// Creates draft Category
let data = {
name: '',
description: '',
status: 'auto-draft',
slug: '',
allowInsert: '',
};
this.createCategory(data)
.then(res => {
this.categoryId = res.id;
this.category = res;
// Fill this.form data with current data.
this.form.name = this.category.name;
this.form.description = this.category.description;
this.form.slug = this.category.slug;
this.form.allowInsert = this.category.allow_insert;
// Pre-fill status with publish to incentivate it
this.form.status = 'publish';
this.isLoading = false;
}
)
.catch(error => console.log(error));
},
clearErrors(attribute) {
this.editFormErrors[attribute] = undefined;
},
cancelBack(){
this.$router.push(this.$routerHelper.getCategoriesPath());
},
labelNewTerms(){
return ( this.form.allowInsert === 'yes' ) ? this.$i18n.get('label_yes') : this.$i18n.get('label_no');
},
},
created(){
if (this.$route.fullPath.split("/").pop() === "new") {
this.createNewCategory();
} else if (this.$route.fullPath.split("/").pop() === "edit") {
this.isLoading = true;
// Obtains current category ID from URL
this.pathArray = this.$route.fullPath.split("/").reverse();
this.categoryId = this.pathArray[1];
this.fetchCategory(this.categoryId).then(res => {
this.category = res.category;
// Fill this.form data with current data.
this.form.name = this.category.name;
this.form.description = this.category.description;
this.form.slug = this.category.slug;
this.form.status = this.category.status;
this.form.allowInsert = this.category.allow_insert;
this.isLoading = false;
});
} }
} }
} }
</script> </script>
<style scoped> <style lang="scss" scoped>
.thumbnail-field {
width: 128px;
height: 128px;
max-width: 128px;
max-height: 128px;
.content {
padding: 10px;
font-size: 0.8em;
}
img {
bottom: 0;
position: absolute;
}
.thumbnail-buttons-row {
display: none;
}
&:hover {
.thumbnail-buttons-row {
display: inline-block;
position: relative;
bottom: 31px;
background-color: rgba(255,255,255,0.8);
padding: 2px 8px;
border-radius: 0px 4px 0px 0px;
}
}
}
</style> </style>

View File

@ -94,7 +94,7 @@
<script> <script>
//import { mapActions } from 'vuex' import { mapActions } from 'vuex'
export default { export default {
name: 'CategoriesList', name: 'CategoriesList',
@ -111,9 +111,9 @@
} }
}, },
methods: { methods: {
// ...mapActions('category', [ ...mapActions('category', [
// 'deleteCategory' 'deleteCategory'
// ]), ]),
deleteOneCategory(categoryId) { deleteOneCategory(categoryId) {
this.$dialog.confirm({ this.$dialog.confirm({
message: this.$i18n.get('info_warning_category_delete'), message: this.$i18n.get('info_warning_category_delete'),

View File

@ -53,6 +53,12 @@ export default {
...mapGetters('item', [ ...mapGetters('item', [
'getItemTitle' 'getItemTitle'
]), ]),
...mapActions('category', [
'fetchCategoryName'
]),
...mapGetters('category', [
'getCategoryName'
]),
generateViewPath() { generateViewPath() {
for (let i = 0; i < this.arrayRealPath.length; i++) { for (let i = 0; i < this.arrayRealPath.length; i++) {
@ -66,12 +72,17 @@ export default {
this.fetchCollectionName(this.arrayRealPath[i]) this.fetchCollectionName(this.arrayRealPath[i])
.then(collectionName => this.arrayViewPath.splice(i, 1, collectionName)) .then(collectionName => this.arrayViewPath.splice(i, 1, collectionName))
.catch((error) => console.log(error)); .catch((error) => console.log(error));
break; break;
case 'items': case 'items':
this.fetchItemTitle(this.arrayRealPath[i]) this.fetchItemTitle(this.arrayRealPath[i])
.then(itemTitle => this.arrayViewPath.splice(i, 1,itemTitle)) .then(itemTitle => this.arrayViewPath.splice(i, 1, itemTitle))
.catch((error) => console.log(error)); .catch((error) => console.log(error));
break; break;
case 'categories':
this.fetchCategoryName(this.arrayRealPath[i])
.then(categoryName => this.arrayViewPath.splice(i, 1, categoryName))
.catch((error) => console.log(error));
break;
} }
} else { } else {

View File

@ -11,6 +11,7 @@ import ItemPage from '../pages/singles/item-page.vue'
import FieldsPage from '../pages/lists/fields-page.vue' import FieldsPage from '../pages/lists/fields-page.vue'
import FiltersPage from '../pages/lists/filters-page.vue' import FiltersPage from '../pages/lists/filters-page.vue'
import CategoriesPage from '../pages/lists/categories-page.vue' import CategoriesPage from '../pages/lists/categories-page.vue'
import CategoryPage from '../pages/singles/category-page.vue'
import EventsPage from '../pages/lists/events-page.vue' import EventsPage from '../pages/lists/events-page.vue'
// Edition Form Components // Edition Form Components
@ -62,6 +63,7 @@ const routes = [
{ path: '/categories', name: 'CategoriesPage', component: CategoriesPage, meta: {title: i18nGet('title_categories_page'), icon: 'shape'} }, { path: '/categories', name: 'CategoriesPage', component: CategoriesPage, meta: {title: i18nGet('title_categories_page'), icon: 'shape'} },
{ path: '/categories/new', name: 'CategoryEditionForm', component: CategoryEditionForm, meta: {title: i18nGet('title_create_category_page'), icon: 'shape'} }, { path: '/categories/new', name: 'CategoryEditionForm', component: CategoryEditionForm, meta: {title: i18nGet('title_create_category_page'), icon: 'shape'} },
{ path: '/categories/:categoryId/edit', name: 'CategoryEditionForm', component: CategoryEditionForm, meta: {title: i18nGet('title_category_edition_page'), icon: 'shape'} }, { path: '/categories/:categoryId/edit', name: 'CategoryEditionForm', component: CategoryEditionForm, meta: {title: i18nGet('title_category_edition_page'), icon: 'shape'} },
{ path: '/categories/:categoryId', name: 'CategoryPage', component: CategoryPage, meta: {title: i18nGet('title_category_page'), icon: 'shape'} },
{ path: '/events', name: 'EventsPage', component: EventsPage, meta: {title: i18nGet('title_events_page'), icon: 'bell'} }, { path: '/events', name: 'EventsPage', component: EventsPage, meta: {title: i18nGet('title_events_page'), icon: 'bell'} },

View File

@ -14,7 +14,7 @@ I18NPlugin.install = function (Vue, options = {}) {
Vue.prototype.$i18n = { Vue.prototype.$i18n = {
get(key) { get(key) {
let string = tainacan_plugin.i18n[key]; let string = tainacan_plugin.i18n[key];
return (string != undefined && string != null && string != '' ) ? string : "Invalid i18n key: " + key; return (string != undefined && string != null && string != '' ) ? string : "Invalid i18n key: " + tainacan_plugin.i18n[key];
}, },
getFrom(entity, key) { getFrom(entity, key) {
if (entity == 'categories') // Temporary hack, while we decide this terminology... if (entity == 'categories') // Temporary hack, while we decide this terminology...
@ -125,7 +125,7 @@ RouterHelperPlugin.install = function (Vue, options = {}) {
return '/items/?' + qs.stringify(query); return '/items/?' + qs.stringify(query);
}, },
getCategoriesPath(query) { getCategoriesPath(query) {
return '/items/?' + qs.stringify(query); return '/categories/?' + qs.stringify(query);
}, },
getCategoryTermsPath(categoryId, query) { getCategoryTermsPath(categoryId, query) {
return '/categoryId/' + categoryId + 'terms/?' + qs.stringify(query); return '/categoryId/' + categoryId + 'terms/?' + qs.stringify(query);

View File

@ -0,0 +1,59 @@
<template>
<div class="columns is-fullheight">
<div class="page-container primary-page">
<div class="card">
<div class="card-content">
<p class="title">
{{ category.name }}
</p>
<p class="subtitle">
{{ category.description }}
</p>
</div>
<footer class="card-footer">
<router-link
class="card-footer-item"
:to="{ path: $routerHelper.getCategoryEditPath(categoryId)}">
{{ $i18n.get('edit') + ' ' + $i18n.get('category') }}
</router-link>
</footer>
</div>
</div>
</div>
</template>
<script>
import { mapActions, mapGetters } from 'vuex';
export default {
name: 'CategoryPage',
data(){
return {
categoryId: Number,
}
},
methods: {
...mapActions('category', [
'fetchCategory'
]),
...mapGetters('category', [
'getCategory'
])
},
computed: {
category(){
return this.getCategory();
}
},
created(){
this.categoryId = parseInt(this.$route.params.categoryId);
this.fetchCategory(this.categoryId);
}
}
</script>
<style scoped>
</style>

View File

@ -126,6 +126,7 @@ return [
'instruction_dragndrop_filters_repository' => __( 'Drag and drop Fields to create Filters on Repository.', 'tainacan' ), 'instruction_dragndrop_filters_repository' => __( 'Drag and drop Fields to create Filters on Repository.', 'tainacan' ),
'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ), 'instruction_delete_selected_collections' => __( 'Delete selected collections', 'tainacan' ),
'instruction_delete_selected_items' => __( 'Delete selected items', 'tainacan' ), 'instruction_delete_selected_items' => __( 'Delete selected items', 'tainacan' ),
'instruction_delete_selected_categories' => __( 'Delete selected categories', 'tainacan' ),
'instruction_image_upload_box' => __( 'Drop an image here or click to upload.', 'tainacan' ), 'instruction_image_upload_box' => __( 'Drop an image here or click to upload.', 'tainacan' ),
'instruction_select_a_status' => __( 'Select a status:', 'tainacan' ), 'instruction_select_a_status' => __( 'Select a status:', 'tainacan' ),
'instruction_select_a_filter_type' => __( 'Select a filter type:', 'tainacan' ), 'instruction_select_a_filter_type' => __( 'Select a filter type:', 'tainacan' ),

View File

@ -173,4 +173,23 @@ class Taxonomy extends Entity {
function set_allow_insert($value) { function set_allow_insert($value) {
$this->set_mapped_property('allow_insert', $value); $this->set_mapped_property('allow_insert', $value);
} }
/**
* Validate Taxonomy
*
* @return bool
*/
function validate() {
if ( ! in_array( $this->get_status(), apply_filters( 'tainacan-status-require-validation', [
'publish',
'future',
'private'
] ) ) ) {
return true;
}
return parent::validate();
}
} }

View File

@ -80,7 +80,7 @@ abstract class Repository {
// First iterate through the native post properties // First iterate through the native post properties
foreach ( $map as $prop => $mapped ) { foreach ( $map as $prop => $mapped ) {
if ( $mapped['map'] != 'meta' && $mapped['map'] != 'meta_multi' && $mapped['map'] != 'thumbnail_id' ) { if ( $mapped['map'] != 'meta' && $mapped['map'] != 'meta_multi' ) {
$obj->WP_Post->{$mapped['map']} = $obj->get_mapped_property( $prop ); $obj->WP_Post->{$mapped['map']} = $obj->get_mapped_property( $prop );
} }
} }

View File

@ -38,7 +38,7 @@ class Taxonomies extends Repository {
'title' => __('Name', 'tainacan'), 'title' => __('Name', 'tainacan'),
'type' => 'string', 'type' => 'string',
'description' => __('Name of the taxonomy', 'tainacan'), 'description' => __('Name of the taxonomy', 'tainacan'),
'on_error' => __('The taxonomy should be a text value and not empty', 'tainacan'), 'on_error' => __('The taxonomy name should be a text value and not empty', 'tainacan'),
'validation' => v::stringType()->notEmpty(), 'validation' => v::stringType()->notEmpty(),
], ],
'description' => [ 'description' => [

View File

@ -2,16 +2,57 @@ import axios from '../../../axios/axios'
export const createCategory = ({commit}, category) => { export const createCategory = ({commit}, category) => {
return new Promise(( resolve, reject ) => { return new Promise(( resolve, reject ) => {
axios.tainacan.post('/taxonomies/', { axios.tainacan.post('/taxonomies', {
name: category.name, name: category.name,
description: category.description, description: category.description,
status: category.status, status: category.status,
slug: category.slug, slug: category.slug,
allow_insert: category.allow_insert allow_insert: category.allowInsert
}) })
.then( res => { .then( res => {
let category = res.data;
console.log(category);
commit('setCategory', category); commit('setCategory', category);
resolve( res.data );
resolve( category );
})
.catch(error => {
reject( error.response );
});
});
};
export const deleteCategory = ({ commit }, categoryId) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.delete(`/taxonomies/${categoryId}`, {
'is_permanently': true
})
.then(res => {
resolve( res.data );
})
.catch(error => {
reject( error )
});
});
};
export const updateCategory = ({ commit }, category) => {
return new Promise(( resolve, reject ) => {
axios.tainacan.patch(`/taxonomies/${category.categoryId}`, {
name: category.name,
description: category.description,
status: category.status,
slug: category.slug ? category.slug : '',
allow_insert: category.allowInsert
})
.then( res => {
let category = res.data;
commit('setCategory', category);
resolve( category );
}) })
.catch(error => { .catch(error => {
reject( error.response ); reject( error.response );
@ -21,15 +62,53 @@ export const createCategory = ({commit}, category) => {
export const fetchCategories = ({ commit }, { page, categoriesPerPage } ) => { export const fetchCategories = ({ commit }, { page, categoriesPerPage } ) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
axios.tainacan.get('/taxonomies?paged='+ page +'&perpage='+ categoriesPerPage) axios.tainacan.get(`/taxonomies?paged=${page}&perpage=${categoriesPerPage}`)
.then(res => { .then(res => {
let categories = res.data; let categories = res.data;
commit('setCategories', categories); commit('setCategories', categories);
resolve({'categories': categories, 'total': res.headers['x-wp-total'] });
resolve({
'categories': categories,
'total': res.headers['x-wp-total']
});
}) })
.catch(error => { .catch(error => {
console.log(error);
reject(error); reject(error);
}); });
}); });
}; };
export const fetchCategory = ({ commit }, categoryId) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/taxonomies/${categoryId}`)
.then(res => {
let category = res.data;
commit('setCategory', category);
resolve({
'category': category
})
})
.catch(error => {
reject(error);
})
});
};
export const fetchCategoryName = ({ commit }, categoryId) => {
return new Promise((resolve, reject) => {
axios.tainacan.get(`/taxonomies/${categoryId}?fetch_only=name`)
.then(res => {
let name = res.data;
commit('setCategoryName');
resolve(name.name)
})
.catch(error => {
reject(error)
})
});
};

View File

@ -5,3 +5,7 @@ export const getCategory = state => {
export const getCategories = state => { export const getCategories = state => {
return state.categories; return state.categories;
}; };
export const getCategoryName = state => {
return state.categoryName;
};

View File

@ -4,7 +4,8 @@ import * as mutations from './mutations';
const state = { const state = {
categories: [], categories: [],
category: null, category: {},
categoryName: String,
}; };
export default { export default {

View File

@ -5,3 +5,7 @@ export const setCategory = (state, category) => {
export const setCategories = (state, categories) => { export const setCategories = (state, categories) => {
state.categories = categories; state.categories = categories;
}; };
export const setCategoryName = (state, name) => {
state.categoryName = name;
};