diff --git a/app/controllers/api/v1/accounts/articles_controller.rb b/app/controllers/api/v1/accounts/articles_controller.rb index 8576ec294..ccaa33a42 100644 --- a/app/controllers/api/v1/accounts/articles_controller.rb +++ b/app/controllers/api/v1/accounts/articles_controller.rb @@ -5,6 +5,7 @@ class Api::V1::Accounts::ArticlesController < Api::V1::Accounts::BaseController before_action :set_current_page, only: [:index] def index + @articles_count = @portal.articles.count @articles = @portal.articles @articles = @articles.search(list_params) if list_params.present? end diff --git a/app/javascript/dashboard/api/helpCenter/portals.js b/app/javascript/dashboard/api/helpCenter/portals.js index 8b8e8f797..2a15fa7bd 100644 --- a/app/javascript/dashboard/api/helpCenter/portals.js +++ b/app/javascript/dashboard/api/helpCenter/portals.js @@ -1,9 +1,16 @@ +/* global axios */ import ApiClient from '../ApiClient'; class PortalsAPI extends ApiClient { constructor() { super('portals', { accountScoped: true }); } + + getArticles({ pageNumber, portalSlug, locale }) { + return axios.get( + `${this.url}/${portalSlug}/articles?page=${pageNumber}&locale=${locale}` + ); + } } export default new PortalsAPI(); diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue index 252215c8c..e88c1eafc 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleItem.vue @@ -4,7 +4,7 @@
- + {{ title }}
@@ -24,16 +24,20 @@ diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleTable.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleTable.vue index 6d397c92e..282e292b3 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleTable.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/ArticleTable.vue @@ -16,20 +16,22 @@
@@ -47,7 +49,7 @@ export default { type: Array, default: () => {}, }, - articleCount: { + totalCount: { type: Number, default: 0, }, @@ -55,10 +57,14 @@ export default { type: Number, default: 1, }, + pageSize: { + type: Number, + default: 15, + }, }, methods: { - onPageChange() { - this.$emit('onPageChange'); + onPageChange(page) { + this.$emit('on-page-change', page); }, }, }; diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue index 3bc51afbe..1ba016edd 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/components/HelpCenterLayout.vue @@ -66,6 +66,12 @@ export default { ...mapGetters({ accountId: 'getCurrentAccountId', }), + portalSlug() { + return this.$route.params.portalSlug; + }, + locale() { + return this.$route.params.locale; + }, accessibleMenuItems() { return [ { @@ -74,7 +80,7 @@ export default { key: 'list_all_locale_articles', count: 199, toState: frontendURL( - `accounts/${this.accountId}/portals/:portalSlug/:locale/articles` + `accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles` ), toolTip: 'All Articles', toStateName: 'list_all_locale_articles', @@ -85,7 +91,7 @@ export default { key: 'mine_articles', count: 112, toState: frontendURL( - `accounts/${this.accountId}/portals/:portalSlug/:locale/articles/mine` + `accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/mine` ), toolTip: 'My articles', toStateName: 'mine_articles', @@ -96,7 +102,7 @@ export default { key: 'list_draft_articles', count: 32, toState: frontendURL( - `accounts/${this.accountId}/portals/:portalSlug/:locale/articles/draft` + `accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/draft` ), toolTip: 'Draft', toStateName: 'list_draft_articles', @@ -107,7 +113,7 @@ export default { key: 'list_archived_articles', count: 10, toState: frontendURL( - `accounts/${this.accountId}/portals/:portalSlug/:locale/articles/archived` + `accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/archived` ), toolTip: 'Archived', toStateName: 'list_archived_articles', diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/mixins/portalMixin.js b/app/javascript/dashboard/routes/dashboard/helpcenter/mixins/portalMixin.js new file mode 100644 index 000000000..9586b8bcd --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/mixins/portalMixin.js @@ -0,0 +1,20 @@ +import { mapGetters } from 'vuex'; +import { frontendURL } from 'dashboard/helper/URLHelper'; +export default { + computed: { + ...mapGetters({ accountId: 'getCurrentAccountId' }), + portalSlug() { + return this.$route.params.portalSlug; + }, + locale() { + return this.$route.params.locale; + }, + }, + methods: { + articleUrl(id) { + return frontendURL( + `accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/${id}` + ); + }, + }, +}; diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/mixins/specs/portalMixin.spec.js b/app/javascript/dashboard/routes/dashboard/helpcenter/mixins/specs/portalMixin.spec.js new file mode 100644 index 000000000..44c1e5b2a --- /dev/null +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/mixins/specs/portalMixin.spec.js @@ -0,0 +1,68 @@ +import { shallowMount, createLocalVue } from '@vue/test-utils'; +import portalMixin from '../portalMixin'; +import Vuex from 'vuex'; +import VueRouter from 'vue-router'; +const localVue = createLocalVue(); +localVue.use(Vuex); +localVue.use(VueRouter); +import ListAllArticles from '../../pages/portals/ListAllPortals.vue'; + +const router = new VueRouter({ + routes: [ + { + path: ':portalSlug/:locale/articles', + name: 'list_all_locale_articles', + component: ListAllArticles, + }, + ], +}); + +describe('portalMixin', () => { + let getters; + let store; + let wrapper; + + beforeEach(() => { + getters = { + getCurrentAccountId: () => 1, + }; + const Component = { + render() {}, + title: 'TestComponent', + mixins: [portalMixin], + router, + }; + store = new Vuex.Store({ getters }); + wrapper = shallowMount(Component, { store, localVue }); + }); + + it('return account id', () => { + expect(wrapper.vm.accountId).toBe(1); + }); + + it('returns portal url', () => { + router.push({ + name: 'list_all_locale_articles', + params: { portalSlug: 'fur-rent', locale: 'en' }, + }); + expect(wrapper.vm.articleUrl(1)).toBe( + '/app/accounts/1/portals/fur-rent/en/articles/1' + ); + }); + + it('returns portal locale', () => { + router.push({ + name: 'list_all_locale_articles', + params: { portalSlug: 'fur-rent', locale: 'es' }, + }); + expect(wrapper.vm.portalSlug).toBe('fur-rent'); + }); + + it('returns portal slug', () => { + router.push({ + name: 'list_all_locale_articles', + params: { portalSlug: 'campaign', locale: 'es' }, + }); + expect(wrapper.vm.portalSlug).toBe('campaign'); + }); +}); diff --git a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue index cb3bb02c1..5b87e4d8f 100644 --- a/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue +++ b/app/javascript/dashboard/routes/dashboard/helpcenter/pages/articles/ListAllArticles.vue @@ -2,26 +2,30 @@
- - - -
+
{{ $t('HELP_CENTER.TABLE.LOADING_MESSAGE') }}
+
diff --git a/app/javascript/dashboard/store/actions.js b/app/javascript/dashboard/store/actions.js deleted file mode 100755 index e69de29bb..000000000 diff --git a/app/javascript/dashboard/store/index.js b/app/javascript/dashboard/store/index.js index 4957a27cc..c4823ec2b 100755 --- a/app/javascript/dashboard/store/index.js +++ b/app/javascript/dashboard/store/index.js @@ -35,6 +35,7 @@ import teamMembers from './modules/teamMembers'; import teams from './modules/teams'; import userNotificationSettings from './modules/userNotificationSettings'; import webhooks from './modules/webhooks'; +import articles from './modules/helpCenterArticles'; Vue.use(Vuex); export default new Vuex.Store({ @@ -73,5 +74,6 @@ export default new Vuex.Store({ teams, userNotificationSettings, webhooks, + articles, }, }); diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/actions.js b/app/javascript/dashboard/store/modules/helpCenterArticles/actions.js index d484dd40c..87d4a886d 100644 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/actions.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/actions.js @@ -1,13 +1,22 @@ -import articlesAPI from 'dashboard/api/helpCenter/articles.js'; +import portalAPI from 'dashboard/api/helpCenter/portals'; +import articlesAPI from 'dashboard/api/helpCenter/articles'; import { throwErrorMessage } from 'dashboard/store/utils/api'; import types from '../../mutation-types'; export const actions = { - index: async ({ commit }) => { + index: async ({ commit }, { pageNumber, portalSlug, locale }) => { try { commit(types.SET_UI_FLAG, { isFetching: true }); - const { data } = await articlesAPI.get(); - const articleIds = data.map(article => article.id); - commit(types.ADD_MANY_ARTICLES, data); + const { + data: { payload, meta }, + } = await portalAPI.getArticles({ + pageNumber, + portalSlug, + locale, + }); + const articleIds = payload.map(article => article.id); + commit(types.CLEAR_ARTICLES); + commit(types.ADD_MANY_ARTICLES, payload); + commit(types.SET_ARTICLES_META, meta); commit(types.ADD_MANY_ARTICLES_ID, articleIds); return articleIds; } catch (error) { @@ -31,6 +40,22 @@ export const actions = { commit(types.SET_UI_FLAG, { isCreating: false }); } }, + + show: async ({ commit }, { id, portalSlug }) => { + commit(types.SET_UI_FLAG, { isFetching: true }); + try { + const response = await portalAPI.getArticle({ id, portalSlug }); + const { + data: { payload }, + } = response; + const { id: articleId } = payload; + commit(types.ADD_ARTICLE, payload); + commit(types.ADD_ARTICLE_ID, articleId); + commit(types.SET_UI_FLAG, { isFetching: false }); + } catch (error) { + commit(types.SET_UI_FLAG, { isFetching: false }); + } + }, update: async ({ commit }, params) => { const articleId = params.id; commit(types.ADD_ARTICLE_FLAG, { diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/getters.js b/app/javascript/dashboard/store/modules/helpCenterArticles/getters.js index 1cd5d94b0..35e32ee5e 100644 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/getters.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/getters.js @@ -1,16 +1,14 @@ export const getters = { - uiFlagsIn: state => helpCenterId => { + uiFlags: state => helpCenterId => { const uiFlags = state.articles.uiFlags.byId[helpCenterId]; if (uiFlags) return uiFlags; return { isFetching: false, isUpdating: false, isDeleting: false }; }, - isFetchingHelpCenterArticles: state => state.uiFlags.isFetching, + isFetching: state => state.uiFlags.isFetching, articleById: (...getterArguments) => articleId => { const [state] = getterArguments; const article = state.articles.byId[articleId]; - if (!article) return undefined; - return article; }, allArticles: (...getterArguments) => { @@ -20,4 +18,7 @@ export const getters = { }); return articles; }, + getMeta: state => { + return state.meta; + }, }; diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/index.js b/app/javascript/dashboard/store/modules/helpCenterArticles/index.js index bbb10f8a2..7648b1ef6 100755 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/index.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/index.js @@ -8,6 +8,10 @@ export const defaultHelpCenterFlags = { isDeleting: false, }; const state = { + meta: { + count: 0, + currentPage: 1, + }, articles: { byId: {}, allIds: [], diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/mutations.js b/app/javascript/dashboard/store/modules/helpCenterArticles/mutations.js index 3657a1e71..9dd0ac28b 100644 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/mutations.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/mutations.js @@ -16,19 +16,28 @@ export const mutations = { ...article, }); }, + [types.CLEAR_ARTICLES]: $state => { + Vue.set($state.articles, 'byId', {}); + Vue.set($state.articles, 'allIds', []); + Vue.set($state.articles, 'uiFlags', {}); + }, [types.ADD_MANY_ARTICLES]($state, articles) { const allArticles = { ...$state.articles.byId }; articles.forEach(article => { allArticles[article.id] = article; }); - Vue.set($state.articles, 'byId', { - allArticles, - }); + Vue.set($state.articles, 'byId', allArticles); }, [types.ADD_MANY_ARTICLES_ID]($state, articleIds) { $state.articles.allIds.push(...articleIds); }, + [types.SET_ARTICLES_META]: ($state, data) => { + const { articles_count: count, current_page: currentPage } = data; + Vue.set($state.meta, 'count', count); + Vue.set($state.meta, 'currentPage', currentPage); + }, + [types.ADD_ARTICLE_ID]: ($state, articleId) => { $state.articles.allIds.push(articleId); }, diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/action.spec.js b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/action.spec.js index c2ff2c74b..0250031b1 100644 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/action.spec.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/action.spec.js @@ -15,10 +15,22 @@ jest.mock('axios'); describe('#actions', () => { describe('#index', () => { it('sends correct actions if API is success', async () => { - axios.get.mockResolvedValue({ data: articleList }); - await actions.index({ commit }); + axios.get.mockResolvedValue({ + data: { + payload: articleList, + meta: { + current_page: '1', + articles_count: 5, + }, + }, + }); + await actions.index( + { commit }, + { pageNumber: 1, portalSlug: 'test', locale: 'en' } + ); expect(commit.mock.calls).toEqual([ [types.default.SET_UI_FLAG, { isFetching: true }], + [types.default.CLEAR_ARTICLES], [ types.default.ADD_MANY_ARTICLES, [ @@ -29,13 +41,22 @@ describe('#actions', () => { }, ], ], + [ + types.default.SET_ARTICLES_META, + { current_page: '1', articles_count: 5 }, + ], [types.default.ADD_MANY_ARTICLES_ID, [1]], [types.default.SET_UI_FLAG, { isFetching: false }], ]); }); it('sends correct actions if API is error', async () => { axios.get.mockRejectedValue({ message: 'Incorrect header' }); - await expect(actions.index({ commit })).rejects.toThrow(Error); + await expect( + actions.index( + { commit }, + { pageNumber: 1, portalSlug: 'test', locale: 'en' } + ) + ).rejects.toThrow(Error); expect(commit.mock.calls).toEqual([ [types.default.SET_UI_FLAG, { isFetching: true }], [types.default.SET_UI_FLAG, { isFetching: false }], diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/fixtures.js b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/fixtures.js index 326f3c2fb..37de5df56 100644 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/fixtures.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/fixtures.js @@ -1,4 +1,8 @@ export default { + meta: { + count: 123, + currentPage: 2, + }, articles: { byId: { 1: { diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/getters.spec.js b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/getters.spec.js index ab271d3a0..73d703641 100644 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/getters.spec.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/getters.spec.js @@ -5,8 +5,8 @@ describe('#getters', () => { beforeEach(() => { state = articles; }); - it('uiFlagsIn', () => { - expect(getters.uiFlagsIn(state)(1)).toEqual({ + it('uiFlags', () => { + expect(getters.uiFlags(state)(1)).toEqual({ isFetching: false, isUpdating: true, isDeleting: false, @@ -34,7 +34,7 @@ describe('#getters', () => { }); }); - it('isFetchingHelpCenters', () => { - expect(getters.isFetchingHelpCenterArticles(state)).toEqual(true); + it('isFetchingArticles', () => { + expect(getters.isFetching(state)).toEqual(true); }); }); diff --git a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/mutation.spec.js b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/mutation.spec.js index ce3b6b33a..077b35aad 100644 --- a/app/javascript/dashboard/store/modules/helpCenterArticles/specs/mutation.spec.js +++ b/app/javascript/dashboard/store/modules/helpCenterArticles/specs/mutation.spec.js @@ -46,6 +46,19 @@ describe('#mutations', () => { }); }); + describe('#ARTICLES_META', () => { + it('add meta to state', () => { + mutations[types.SET_ARTICLES_META](state, { + articles_count: 3, + current_page: 1, + }); + expect(state.meta).toEqual({ + count: 3, + currentPage: 1, + }); + }); + }); + describe('#ADD_ARTICLE_ID', () => { it('add valid article id to state', () => { mutations[types.ADD_ARTICLE_ID](state, 3); @@ -87,4 +100,13 @@ describe('#mutations', () => { expect(state.articles.byId[2]).toEqual(undefined); }); }); + + describe('#CLEAR_ARTICLES', () => { + it('clears articles', () => { + mutations[types.CLEAR_ARTICLES](state); + expect(state.articles.allIds).toEqual([]); + expect(state.articles.byId).toEqual({}); + expect(state.articles.uiFlags).toEqual({}); + }); + }); }); diff --git a/app/javascript/dashboard/store/mutation-types.js b/app/javascript/dashboard/store/mutation-types.js index 034190507..c9073d58a 100755 --- a/app/javascript/dashboard/store/mutation-types.js +++ b/app/javascript/dashboard/store/mutation-types.js @@ -226,8 +226,10 @@ export default { ADD_ARTICLE_ID: 'ADD_ARTICLE_ID', ADD_MANY_ARTICLES: 'ADD_MANY_ARTICLES', ADD_MANY_ARTICLES_ID: 'ADD_MANY_ARTICLES_ID', + SET_ARTICLES_META: 'SET_ARTICLES_META', ADD_ARTICLE_FLAG: 'ADD_ARTICLE_FLAG', UPDATE_ARTICLE: 'UPDATE_ARTICLE', + CLEAR_ARTICLES: 'CLEAR_ARTICLES', REMOVE_ARTICLE: 'REMOVE_ARTICLE', REMOVE_ARTICLE_ID: 'REMOVE_ARTICLE_ID', SET_UI_FLAG: 'SET_UI_FLAG', diff --git a/app/views/api/v1/accounts/articles/_article.json.jbuilder b/app/views/api/v1/accounts/articles/_article.json.jbuilder index e3b1af52b..93a7bbf84 100644 --- a/app/views/api/v1/accounts/articles/_article.json.jbuilder +++ b/app/views/api/v1/accounts/articles/_article.json.jbuilder @@ -5,6 +5,7 @@ json.content article.content json.description article.description json.status article.status json.account_id article.account_id +json.updated_at article.updated_at.to_i if article.portal.present? json.portal do diff --git a/app/views/api/v1/accounts/articles/index.json.jbuilder b/app/views/api/v1/accounts/articles/index.json.jbuilder index 89634e32a..fef8dc521 100644 --- a/app/views/api/v1/accounts/articles/index.json.jbuilder +++ b/app/views/api/v1/accounts/articles/index.json.jbuilder @@ -4,5 +4,5 @@ end json.meta do json.current_page @current_page - json.articles_count @articles.size + json.articles_count @articles_count end diff --git a/spec/controllers/api/v1/accounts/articles_controller_spec.rb b/spec/controllers/api/v1/accounts/articles_controller_spec.rb index 57d4f8feb..1e98af260 100644 --- a/spec/controllers/api/v1/accounts/articles_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/articles_controller_spec.rb @@ -190,7 +190,7 @@ RSpec.describe 'Api::V1::Accounts::Articles', type: :request do expect(response).to have_http_status(:success) json_response = JSON.parse(response.body) expect(json_response['payload'].count).to be 1 - expect(json_response['meta']['articles_count']).to be json_response['payload'].size + expect(json_response['meta']['articles_count']).to be 2 end end