Compare commits

...

7 commits

Author SHA1 Message Date
iamsivin
06dff64ee9 fix: failing test 2022-09-02 20:20:17 +05:30
iamsivin
100c676798 chore: Code clean up 2022-09-02 20:04:20 +05:30
iamsivin
50a6e91142 chore: Adds empty text 2022-09-02 19:41:30 +05:30
iamsivin
de413a35a6 chore: MInor fixes 2022-09-02 19:30:46 +05:30
Sivin Varghese
e9592d2685
Merge branch 'develop' into feat/edit-delete-category 2022-09-02 18:50:30 +05:30
iamsivin
c640b7134f chore: Category store fixes 2022-09-02 18:40:52 +05:30
iamsivin
ba0fc3552d feat: Adds the ability to edit/delete category 2022-09-02 17:43:57 +05:30
17 changed files with 663 additions and 37 deletions

View file

@ -15,8 +15,11 @@ class CategoriesAPI extends PortalsAPI {
return axios.post(`${this.url}/${portalSlug}/categories`, categoryObj); return axios.post(`${this.url}/${portalSlug}/categories`, categoryObj);
} }
update({ portalSlug, categoryObj }) { update({ portalSlug, categoryId, categoryObj }) {
return axios.patch(`${this.url}/${portalSlug}/categories`, categoryObj); return axios.patch(
`${this.url}/${portalSlug}/categories/${categoryId}`,
categoryObj
);
} }
delete({ portalSlug, categoryId }) { delete({ portalSlug, categoryId }) {

View file

@ -139,6 +139,21 @@
"TITLE": "Locales" "TITLE": "Locales"
} }
}, },
"CATEGORIES": {
"TITLE": "Categories in",
"NEW_CATEGORY": "New category",
"TABLE": {
"NAME": "Name",
"DESCRIPTION": "Description",
"LOCALE": "Locale",
"ARTICLE_COUNT": "No. of articles",
"ACTION_BUTTON": {
"EDIT": "Edit category",
"DELETE": "Delete category"
},
"EMPTY_TEXT": "No categories found"
}
},
"EDIT_BASIC_INFO": { "EDIT_BASIC_INFO": {
"BUTTON_TEXT": "Update basic settings" "BUTTON_TEXT": "Update basic settings"
} }
@ -353,6 +368,43 @@
"SUCCESS_MESSAGE": "Category created successfully", "SUCCESS_MESSAGE": "Category created successfully",
"ERROR_MESSAGE": "Unable to create category" "ERROR_MESSAGE": "Unable to create category"
} }
},
"EDIT": {
"TITLE": "Edit a category",
"SUB_TITLE": "Editing a category will update the category in the public facing portal.",
"PORTAL": "Portal",
"LOCALE": "Locale",
"NAME": {
"LABEL": "Name",
"PLACEHOLDER": "Category name",
"HELP_TEXT": "The category name will be used in the public facing portal to categorize articles.",
"ERROR": "Name is required"
},
"SLUG": {
"LABEL": "Slug",
"PLACEHOLDER": "Category slug for urls",
"HELP_TEXT": "app.chatwoot.com/portal/my-portal/en-US/categories/my-slug",
"ERROR": "Slug is required"
},
"DESCRIPTION": {
"LABEL": "Description",
"PLACEHOLDER": "Give a short description about the category.",
"ERROR": "Description is required"
},
"BUTTONS": {
"CREATE": "Update category",
"CANCEL": "Cancel"
},
"API": {
"SUCCESS_MESSAGE": "Category updated successfully",
"ERROR_MESSAGE": "Unable to update category"
}
},
"DELETE": {
"API": {
"SUCCESS_MESSAGE": "Category deleted successfully",
"ERROR_MESSAGE": "Unable to delete category"
}
} }
} }
} }

View file

@ -37,6 +37,7 @@
/> />
<add-category <add-category
v-if="showAddCategoryModal" v-if="showAddCategoryModal"
:show.sync="showAddCategoryModal"
:portal-name="selectedPortalName" :portal-name="selectedPortalName"
:locale="selectedPortalLocale" :locale="selectedPortalLocale"
@cancel="onClickCloseAddCategoryModal" @cancel="onClickCloseAddCategoryModal"
@ -56,7 +57,7 @@ import CommandBar from 'dashboard/routes/dashboard/commands/commandbar.vue';
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal'; import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal';
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel'; import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel';
import portalMixin from '../mixins/portalMixin'; import portalMixin from '../mixins/portalMixin';
import AddCategory from '../components/AddCategory.vue'; import AddCategory from '../pages/categories/AddCategory';
export default { export default {
components: { components: {
@ -182,6 +183,7 @@ export default {
]; ];
}, },
additionalSecondaryMenuItems() { additionalSecondaryMenuItems() {
if (!this.selectedPortal) return [];
return [ return [
{ {
icon: 'folder', icon: 'folder',

View file

@ -243,6 +243,7 @@ export default {
this.$emit('open-site'); this.$emit('open-site');
}, },
openSettings() { openSettings() {
this.fetchPortalsAndItsCategories();
this.navigateToPortalEdit(); this.navigateToPortalEdit();
}, },
onClickOpenDeleteModal(portal) { onClickOpenDeleteModal(portal) {
@ -252,6 +253,13 @@ export default {
closeDeletePopup() { closeDeletePopup() {
this.showDeleteConfirmationPopup = false; this.showDeleteConfirmationPopup = false;
}, },
fetchPortalsAndItsCategories() {
this.$store.dispatch('portals/index').then(() => {
this.$store.dispatch('categories/index', {
portalSlug: this.portal.slug,
});
});
},
async onClickDeletePortal() { async onClickDeletePortal() {
const { slug } = this.selectedPortalForDelete; const { slug } = this.selectedPortalForDelete;
try { try {

View file

@ -92,8 +92,16 @@ export default {
this.selectedLocale = this.locale || this.portal?.meta?.default_locale; this.selectedLocale = this.locale || this.portal?.meta?.default_locale;
}, },
methods: { methods: {
fetchPortalsAndItsCategories() {
this.$store.dispatch('portals/index').then(() => {
this.$store.dispatch('categories/index', {
portalSlug: this.portal.slug,
});
});
},
onClick(event, code, portal) { onClick(event, code, portal) {
event.preventDefault(); event.preventDefault();
this.fetchPortalsAndItsCategories();
this.$router.push({ this.$router.push({
name: 'list_all_locale_articles', name: 'list_all_locale_articles',
params: { params: {

View file

@ -77,6 +77,12 @@ const portalRoutes = [
component: EditPortalCustomization, component: EditPortalCustomization,
roles: ['administrator'], roles: ['administrator'],
}, },
{
path: getPortalRoute(':portalSlug/edit/:locale/categories'),
name: 'list_all_locale_categories',
roles: ['administrator', 'agent'],
component: ListAllCategories,
},
], ],
}, },
]; ];
@ -125,7 +131,7 @@ const articleRoutes = [
const categoryRoutes = [ const categoryRoutes = [
{ {
path: getPortalRoute(':portalSlug/:locale/categories'), path: getPortalRoute(':portalSlug/:locale/categories'),
name: 'list_all_locale_categories', name: 'all_locale_categories',
roles: ['administrator', 'agent'], roles: ['administrator', 'agent'],
component: ListAllCategories, component: ListAllCategories,
}, },

View file

@ -132,11 +132,12 @@ export default {
}, },
async addCategory() { async addCategory() {
const { name, slug, description } = this; const { name, slug, description, locale } = this;
const data = { const data = {
name, name,
slug, slug,
description, description,
locale,
}; };
this.$v.$touch(); this.$v.$touch();
if (this.$v.$invalid) { if (this.$v.$invalid) {

View file

@ -0,0 +1,123 @@
<template>
<div>
<table>
<thead>
<tr>
<th scope="col">
{{ $t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.TABLE.NAME') }}
</th>
<th scope="col">
{{ $t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.TABLE.DESCRIPTION') }}
</th>
<th scope="col">
{{ $t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.TABLE.LOCALE') }}
</th>
<th scope="col">
{{ $t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.TABLE.ARTICLE_COUNT') }}
</th>
<th scope="col" />
</tr>
</thead>
<tr>
<td colspan="100%" class="horizontal-line" />
</tr>
<tbody>
<tr v-for="category in categories" :key="category.id">
<td>
<span>{{ category.name }}</span>
</td>
<td>
<span>{{ category.description }}</span>
</td>
<td>
<span>{{ category.locale }}</span>
</td>
<td>
<span>{{ category.meta.articles_count }}</span>
</td>
<td>
<woot-button
v-tooltip.top-end="
$t(
'HELP_CENTER.PORTAL.EDIT.CATEGORIES.TABLE.ACTION_BUTTON.EDIT'
)
"
size="tiny"
variant="smooth"
icon="edit"
color-scheme="secondary"
@click="editCategory(category)"
/>
<woot-button
v-tooltip.top-end="
$t(
'HELP_CENTER.PORTAL.EDIT.CATEGORIES.TABLE.ACTION_BUTTON.DELETE'
)
"
size="tiny"
variant="smooth"
icon="delete"
color-scheme="alert"
@click="deleteCategory(category.id)"
/>
</td>
</tr>
</tbody>
</table>
<p v-if="categories.length === 0" class="empty-text">
{{ $t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.TABLE.EMPTY_TEXT') }}
</p>
</div>
</template>
<script>
export default {
props: {
categories: {
type: Array,
default: () => [],
},
},
methods: {
editCategory(category) {
this.$emit('edit', category);
},
deleteCategory(categoryId) {
this.$emit('delete', categoryId);
},
},
};
</script>
<style lang="scss" scoped>
table {
thead tr th {
font-size: var(--font-size-small);
font-weight: var(--font-weight-medium);
text-transform: none;
color: var(--s-600);
padding-left: 0;
padding-top: 0;
}
tbody tr {
border-bottom: 0;
td {
font-size: var(--font-size-small);
padding-left: 0;
}
}
}
.horizontal-line {
border-bottom: 1px solid var(--color-border);
}
.empty-text {
display: flex;
justify-content: center;
color: var(--s-500);
font-size: var(--font-size-default);
margin-top: var(--space-three);
}
</style>

View file

@ -1,3 +1,198 @@
<template> <template>
<div>Component to edit a category</div> <woot-modal :show.sync="show" :on-close="onClose">
<woot-modal-header
:header-title="$t('HELP_CENTER.CATEGORY.EDIT.TITLE')"
:header-content="$t('HELP_CENTER.CATEGORY.EDIT.SUB_TITLE')"
/>
<form class="row" @submit.prevent="onUpdate">
<div class="medium-12 columns">
<div class="row article-info">
<div class="columns medium-6">
<label>
<span>{{ $t('HELP_CENTER.CATEGORY.EDIT.PORTAL') }}</span>
<p class="value">{{ portalName }}</p>
</label>
</div>
<div class="columns medium-6">
<label>
<span>{{ $t('HELP_CENTER.CATEGORY.EDIT.LOCALE') }}</span>
<p class="value">{{ locale }}</p>
</label>
</div>
</div>
<woot-input
v-model.trim="name"
:class="{ error: $v.name.$error }"
class="medium-12 columns"
:error="nameError"
:label="$t('HELP_CENTER.CATEGORY.EDIT.NAME.LABEL')"
:placeholder="$t('HELP_CENTER.CATEGORY.EDIT.NAME.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.CATEGORY.EDIT.NAME.HELP_TEXT')"
@input="onNameChange"
/>
<woot-input
v-model.trim="slug"
:class="{ error: $v.slug.$error }"
class="medium-12 columns"
:error="slugError"
:label="$t('HELP_CENTER.CATEGORY.EDIT.SLUG.LABEL')"
:placeholder="$t('HELP_CENTER.CATEGORY.EDIT.SLUG.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.CATEGORY.EDIT.SLUG.HELP_TEXT')"
@input="$v.slug.$touch"
/>
<label>
{{ $t('HELP_CENTER.CATEGORY.EDIT.DESCRIPTION.LABEL') }}
<textarea
v-model="description"
rows="3"
type="text"
:placeholder="
$t('HELP_CENTER.CATEGORY.EDIT.DESCRIPTION.PLACEHOLDER')
"
/>
</label>
<div class="medium-12 columns">
<div class="modal-footer justify-content-end w-full">
<woot-button class="button clear" @click.prevent="onClose">
{{ $t('HELP_CENTER.CATEGORY.EDIT.BUTTONS.CANCEL') }}
</woot-button>
<woot-button @click="editCategory">
{{ $t('HELP_CENTER.CATEGORY.EDIT.BUTTONS.CREATE') }}
</woot-button>
</div>
</div>
</div>
</form>
</woot-modal>
</template> </template>
<script>
import alertMixin from 'shared/mixins/alertMixin';
import { required, minLength } from 'vuelidate/lib/validators';
import { convertToCategorySlug } from 'dashboard/helper/commons.js';
export default {
mixins: [alertMixin],
props: {
show: {
type: Boolean,
default: true,
},
portalName: {
type: String,
default: '',
},
locale: {
type: String,
default: '',
},
category: {
type: Object,
default: () => {},
},
selectedPortalSlug: {
type: String,
default: '',
},
},
data() {
return {
id: this.category.id,
name: '',
slug: '',
description: '',
};
},
validations: {
name: {
required,
minLength: minLength(2),
},
slug: {
required,
},
},
computed: {
nameError() {
if (this.$v.name.$error) {
return this.$t('HELP_CENTER.CATEGORY.ADD.NAME.ERROR');
}
return '';
},
slugError() {
if (this.$v.slug.$error) {
return this.$t('HELP_CENTER.CATEGORY.ADD.SLUG.ERROR');
}
return '';
},
},
mounted() {
this.updateDataFromStore();
},
methods: {
updateDataFromStore() {
const { category } = this;
this.name = category.name;
this.slug = category.slug;
this.description = category.description;
},
onNameChange() {
this.slug = convertToCategorySlug(this.name);
},
onUpdate() {
this.$emit('update');
},
onClose() {
this.$emit('cancel');
},
async editCategory() {
const { id, name, slug, description } = this;
const data = {
id,
name,
slug,
description,
};
this.$v.$touch();
if (this.$v.$invalid) {
return;
}
try {
await this.$store.dispatch('categories/update', {
portalSlug: this.selectedPortalSlug,
categoryId: id,
categoryObj: data,
});
this.alertMessage = this.$t(
'HELP_CENTER.CATEGORY.EDIT.API.SUCCESS_MESSAGE'
);
this.onClose();
} catch (error) {
const errorMessage = error?.message;
this.alertMessage =
errorMessage ||
this.$t('HELP_CENTER.CATEGORY.EDIT.API.ERROR_MESSAGE');
} finally {
this.showAlert(this.alertMessage);
}
},
},
};
</script>
<style scoped lang="scss">
.article-info {
width: 100%;
margin: 0 0 var(--space-normal);
.value {
color: var(--s-600);
}
}
.input-container::v-deep {
margin: 0 0 var(--space-normal);
input {
margin-bottom: 0;
}
}
</style>

View file

@ -1,3 +1,190 @@
<template> <template>
<div>Component to list all categories</div> <div class="category-list--container">
<header>
<div class="header-left--wrap">
<label class="sub-block-title header-text">{{
$t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.TITLE')
}}</label>
<select :value="currentLocaleCode" @change="changeCurrentCategory">
<option
v-for="allowedLocaleCode in allowedLocaleCodes"
:key="allowedLocaleCode"
:value="allowedLocaleCode"
>
{{ allowedLocaleCode }}
</option>
</select>
</div>
<div class="header-right--wrap">
<woot-button
size="small"
variant="smooth"
color-scheme="primary"
icon="add"
@click="openAddCategoryModal"
>
{{ $t('HELP_CENTER.PORTAL.EDIT.CATEGORIES.NEW_CATEGORY') }}
</woot-button>
</div>
</header>
<div class="category-list">
<category-list-item
:categories="categoryByLocaleCode"
@delete="deleteCategory"
@edit="openEditCategoryModal"
/>
</div>
<edit-category
v-if="showEditCategoryModal"
:show.sync="showEditCategoryModal"
:portal-name="currentPortalName"
:locale="selectedCategory.locale"
:category="selectedCategory"
:selected-portal-slug="currentPortalSlug"
@cancel="closeEditCategoryModal"
/>
<add-category
v-if="showAddCategoryModal"
:show.sync="showAddCategoryModal"
:portal-name="currentPortalName"
:locale="currentLocaleCode"
@cancel="closeAddCategoryModal"
/>
</div>
</template> </template>
<script>
import { mapGetters } from 'vuex';
import alertMixin from 'shared/mixins/alertMixin';
import portalMixin from '../../mixins/portalMixin';
import CategoryListItem from './CategoryListItem';
import AddCategory from './AddCategory';
import EditCategory from './EditCategory';
export default {
components: {
CategoryListItem,
AddCategory,
EditCategory,
},
mixins: [alertMixin, portalMixin],
data() {
return {
selectedCategory: {},
selectedLocaleCode: '',
currentLocaleCode: 'en',
showEditCategoryModal: false,
showAddCategoryModal: false,
alertMessage: '',
};
},
computed: {
...mapGetters({
portals: 'portals/allPortals',
meta: 'portals/getMeta',
isFetching: 'portals/isFetchingPortals',
}),
currentPortalSlug() {
return this.$route.params.portalSlug;
},
categoryByLocaleCode() {
return this.$store.getters['categories/categoriesByLocaleCode'](
this.currentLocaleCode
);
},
currentPortal() {
const slug = this.currentPortalSlug;
if (slug) return this.$store.getters['portals/portalBySlug'](slug);
return this.$store.getters['portals/allPortals'][0];
},
currentPortalName() {
return this.currentPortal ? this.currentPortal.name : '';
},
currentPortalLocale() {
return this.currentPortal ? this.currentPortal?.meta?.default_locale : '';
},
allLocales() {
return this.currentPortal
? this.currentPortal.config.allowed_locales
: [];
},
allowedLocaleCodes() {
return this.allLocales.map(locale => locale.code);
},
},
methods: {
openAddCategoryModal() {
this.showAddCategoryModal = true;
},
openEditCategoryModal(category) {
this.selectedCategory = category;
this.showEditCategoryModal = true;
},
closeAddCategoryModal() {
this.showAddCategoryModal = false;
},
closeEditCategoryModal() {
this.showEditCategoryModal = false;
},
async deleteCategory(categoryId) {
try {
await this.$store.dispatch('categories/delete', {
portalSlug: this.currentPortalSlug,
categoryId: categoryId,
});
this.alertMessage = this.$t(
'HELP_CENTER.CATEGORY.DELETE.API.SUCCESS_MESSAGE'
);
} catch (error) {
const errorMessage = error?.message;
this.alertMessage =
errorMessage ||
this.$t('HELP_CENTER.CATEGORY.DELETE.API.ERROR_MESSAGE');
} finally {
this.showAlert(this.alertMessage);
}
},
changeCurrentCategory(event) {
const localeCode = event.target.value;
this.currentLocaleCode = localeCode;
},
},
};
</script>
<style lang="scss" scoped>
.category-list--container {
width: 100%;
padding-left: var(--space-normal);
header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-normal);
.header-left--wrap {
display: flex;
align-items: center;
width: 100%;
.header-text {
margin-right: var(--space-slab);
margin-bottom: 0;
}
}
.header-right--wrap {
flex: none;
align-items: center;
}
select {
height: var(--space-large);
max-width: 16rem;
margin-bottom: 0;
padding-top: var(--space-micro);
padding-bottom: var(--space-micro);
}
}
}
</style>

View file

@ -49,7 +49,8 @@ export default {
}), }),
currentPortal() { currentPortal() {
const slug = this.$route.params.portalSlug; const slug = this.$route.params.portalSlug;
return this.$store.getters['portals/portalBySlug'](slug); if (slug) return this.$store.getters['portals/portalBySlug'](slug);
return this.$store.getters['portals/allPortals'][0];
}, },
tabs() { tabs() {
const tabs = [ const tabs = [
@ -64,7 +65,7 @@ export default {
), ),
}, },
{ {
key: 'categories', key: `list_all_locale_categories`,
name: this.$t('HELP_CENTER.PORTAL.EDIT.TABS.CATEGORY_SETTINGS.TITLE'), name: this.$t('HELP_CENTER.PORTAL.EDIT.TABS.CATEGORY_SETTINGS.TITLE'),
}, },
{ {
@ -81,6 +82,9 @@ export default {
portalName() { portalName() {
return this.currentPortal ? this.currentPortal.name : ''; return this.currentPortal ? this.currentPortal.name : '';
}, },
currentPortalLocale() {
return this.currentPortal ? this.currentPortal?.meta?.default_locale : '';
},
}, },
methods: { methods: {
onTabChange(index) { onTabChange(index) {
@ -89,7 +93,7 @@ export default {
this.$router.push({ this.$router.push({
name: nextRoute, name: nextRoute,
params: { portalSlug: slug }, params: { portalSlug: slug, locale: this.currentPortalLocale },
}); });
}, },
}, },
@ -99,14 +103,7 @@ export default {
.wrapper { .wrapper {
flex: 1; flex: 1;
} }
.container { ::v-deep .tabs {
display: flex; padding-left: 0;
flex: 1;
}
.wizard-box {
border-right: 1px solid var(--s-25);
::v-deep .item {
background: var(--white);
}
} }
</style> </style>

View file

@ -9,6 +9,7 @@ export const actions = {
const { const {
data: { payload }, data: { payload },
} = await categoriesAPI.get({ portalSlug }); } = await categoriesAPI.get({ portalSlug });
commit(types.CLEAR_CATEGORIES);
const categoryIds = payload.map(category => category.id); const categoryIds = payload.map(category => category.id);
commit(types.ADD_MANY_CATEGORIES, payload); commit(types.ADD_MANY_CATEGORIES, payload);
commit(types.ADD_MANY_CATEGORIES_ID, categoryIds); commit(types.ADD_MANY_CATEGORIES_ID, categoryIds);
@ -39,8 +40,7 @@ export const actions = {
} }
}, },
update: async ({ commit }, portalSlug, categoryObj) => { update: async ({ commit }, { portalSlug, categoryId, categoryObj }) => {
const categoryId = categoryObj.id;
commit(types.ADD_CATEGORY_FLAG, { commit(types.ADD_CATEGORY_FLAG, {
uiFlags: { uiFlags: {
isUpdating: true, isUpdating: true,
@ -48,8 +48,14 @@ export const actions = {
categoryId, categoryId,
}); });
try { try {
const { data } = await categoriesAPI.update({ portalSlug, categoryObj }); const {
commit(types.UPDATE_CATEGORY, data); data: { payload },
} = await categoriesAPI.update({
portalSlug,
categoryId,
categoryObj,
});
commit(types.UPDATE_CATEGORY, payload);
return categoryId; return categoryId;
} catch (error) { } catch (error) {
return throwErrorMessage(error); return throwErrorMessage(error);
@ -63,7 +69,7 @@ export const actions = {
} }
}, },
delete: async ({ commit }, portalSlug, categoryId) => { delete: async ({ commit }, { portalSlug, categoryId }) => {
commit(types.ADD_CATEGORY_FLAG, { commit(types.ADD_CATEGORY_FLAG, {
uiFlags: { uiFlags: {
isDeleting: true, isDeleting: true,

View file

@ -18,6 +18,13 @@ export const getters = {
}); });
return categories; return categories;
}, },
categoriesByLocaleCode: (...getterArguments) => localeCode => {
const [state, _getters] = getterArguments;
const categories = state.categories.allIds.map(id => {
return _getters.categoryById(id);
});
return categories.filter(category => category.locale === localeCode);
},
getMeta: state => { getMeta: state => {
return state.meta; return state.meta;
}, },

View file

@ -19,7 +19,7 @@ export const mutations = {
[types.CLEAR_CATEGORIES]: $state => { [types.CLEAR_CATEGORIES]: $state => {
Vue.set($state.categories, 'byId', {}); Vue.set($state.categories, 'byId', {});
Vue.set($state.categories, 'allIds', []); Vue.set($state.categories, 'allIds', []);
Vue.set($state.categories, 'uiFlags', {}); Vue.set($state.categories.uiFlags, 'byId', {});
}, },
[types.ADD_MANY_CATEGORIES]($state, categories) { [types.ADD_MANY_CATEGORIES]($state, categories) {
const allCategories = { ...$state.categories.byId }; const allCategories = { ...$state.categories.byId };

View file

@ -13,6 +13,7 @@ describe('#actions', () => {
await actions.index({ commit }, { portalSlug: 'room-rental' }); await actions.index({ commit }, { portalSlug: 'room-rental' });
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
[types.default.SET_UI_FLAG, { isFetching: true }], [types.default.SET_UI_FLAG, { isFetching: true }],
[types.default.CLEAR_CATEGORIES],
[types.default.ADD_MANY_CATEGORIES, categoriesPayload.payload], [types.default.ADD_MANY_CATEGORIES, categoriesPayload.payload],
[types.default.ADD_MANY_CATEGORIES_ID, [1, 2]], [types.default.ADD_MANY_CATEGORIES_ID, [1, 2]],
[types.default.SET_UI_FLAG, { isFetching: false }], [types.default.SET_UI_FLAG, { isFetching: false }],
@ -57,21 +58,34 @@ describe('#actions', () => {
describe('#update', () => { describe('#update', () => {
it('sends correct actions if API is success', async () => { it('sends correct actions if API is success', async () => {
axios.patch.mockResolvedValue({ data: categoriesPayload.payload[0] }); axios.patch.mockResolvedValue({ data: categoriesPayload });
await actions.update( await actions.update(
{ commit }, { commit },
'web-docs', {
categoriesPayload.payload[0] portalSlug: 'room-rental',
categoryId: 1,
categoryObj: categoriesPayload.payload[0],
}
); );
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
[ [
types.default.ADD_CATEGORY_FLAG, types.default.ADD_CATEGORY_FLAG,
{ uiFlags: { isUpdating: true }, categoryId: 1 }, {
uiFlags: {
isUpdating: true,
},
categoryId: 1,
},
], ],
[types.default.UPDATE_CATEGORY, categoriesPayload.payload[0]], [types.default.UPDATE_CATEGORY, categoriesPayload.payload],
[ [
types.default.ADD_CATEGORY_FLAG, types.default.ADD_CATEGORY_FLAG,
{ uiFlags: { isUpdating: false }, categoryId: 1 }, {
uiFlags: {
isUpdating: false,
},
categoryId: 1,
},
], ],
]); ]);
}); });
@ -79,7 +93,14 @@ describe('#actions', () => {
it('sends correct actions if API is error', async () => { it('sends correct actions if API is error', async () => {
axios.patch.mockRejectedValue({ message: 'Incorrect header' }); axios.patch.mockRejectedValue({ message: 'Incorrect header' });
await expect( await expect(
actions.update({ commit }, 'web-docs', categoriesPayload.payload[0]) actions.update(
{ commit },
{
portalSlug: 'room-rental',
categoryId: 1,
categoryObj: categoriesPayload.payload[0],
}
)
).rejects.toThrow(Error); ).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
[ [
@ -96,11 +117,13 @@ describe('#actions', () => {
describe('#delete', () => { describe('#delete', () => {
it('sends correct actions if API is success', async () => { it('sends correct actions if API is success', async () => {
axios.delete.mockResolvedValue({ data: categoriesPayload.payload[0] }); axios.delete.mockResolvedValue({ data: categoriesPayload });
await actions.delete( await actions.delete(
{ commit }, { commit },
'portal-slug', {
categoriesPayload.payload[0].id portalSlug: 'room-rental',
categoryId: categoriesPayload.payload[0].id,
}
); );
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
[ [
@ -120,8 +143,10 @@ describe('#actions', () => {
await expect( await expect(
actions.delete( actions.delete(
{ commit }, { commit },
'portal-slug', {
categoriesPayload.payload[0].id portalSlug: 'room-rental',
categoryId: categoriesPayload.payload[0].id,
}
) )
).rejects.toThrow(Error); ).rejects.toThrow(Error);
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([

View file

@ -19,6 +19,10 @@ describe('#getters', () => {
); );
}); });
it('categoriesByLocaleCode', () => {
expect(getters.categoriesByLocaleCode(state, getters)('en_US')).toEqual([]);
});
it('isFetchingCategories', () => { it('isFetchingCategories', () => {
expect(getters.isFetching(state)).toEqual(true); expect(getters.isFetching(state)).toEqual(true);
}); });

View file

@ -234,12 +234,14 @@ export default {
REMOVE_ARTICLE: 'REMOVE_ARTICLE', REMOVE_ARTICLE: 'REMOVE_ARTICLE',
REMOVE_ARTICLE_ID: 'REMOVE_ARTICLE_ID', REMOVE_ARTICLE_ID: 'REMOVE_ARTICLE_ID',
SET_UI_FLAG: 'SET_UI_FLAG', SET_UI_FLAG: 'SET_UI_FLAG',
// Help Center -- Categories // Help Center -- Categories
ADD_CATEGORY: 'ADD_CATEGORY', ADD_CATEGORY: 'ADD_CATEGORY',
ADD_CATEGORY_ID: 'ADD_CATEGORY_ID', ADD_CATEGORY_ID: 'ADD_CATEGORY_ID',
ADD_MANY_CATEGORIES: 'ADD_MANY_CATEGORIES', ADD_MANY_CATEGORIES: 'ADD_MANY_CATEGORIES',
ADD_MANY_CATEGORIES_ID: 'ADD_MANY_CATEGORIES_ID', ADD_MANY_CATEGORIES_ID: 'ADD_MANY_CATEGORIES_ID',
ADD_CATEGORY_FLAG: 'ADD_CATEGORY_FLAG', ADD_CATEGORY_FLAG: 'ADD_CATEGORY_FLAG',
CLEAR_CATEGORIES: 'CLEAR_CATEGORIES',
UPDATE_CATEGORY: 'UPDATE_CATEGORY', UPDATE_CATEGORY: 'UPDATE_CATEGORY',
REMOVE_CATEGORY: 'REMOVE_CATEGORY', REMOVE_CATEGORY: 'REMOVE_CATEGORY',
REMOVE_CATEGORY_ID: 'REMOVE_CATEGORY_ID', REMOVE_CATEGORY_ID: 'REMOVE_CATEGORY_ID',