feat: Portals store integration (#5185)
This commit is contained in:
parent
052422ed03
commit
20f3568583
30 changed files with 982 additions and 413 deletions
|
@ -53,3 +53,4 @@ exclude_patterns:
|
||||||
- 'app/javascript/dashboard/i18n/index.js'
|
- 'app/javascript/dashboard/i18n/index.js'
|
||||||
- 'app/javascript/widget/i18n/index.js'
|
- 'app/javascript/widget/i18n/index.js'
|
||||||
- 'app/javascript/survey/i18n/index.js'
|
- 'app/javascript/survey/i18n/index.js'
|
||||||
|
- 'app/javascript/shared/constants/locales.js'
|
||||||
|
|
|
@ -1,9 +1,18 @@
|
||||||
import ApiClient from '../ApiClient';
|
/* global axios */
|
||||||
|
|
||||||
class ArticlesAPI extends ApiClient {
|
import PortalsAPI from './portals';
|
||||||
|
|
||||||
|
class ArticlesAPI extends PortalsAPI {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('articles', { accountScoped: true });
|
super('articles', { accountScoped: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getArticles({ pageNumber, portalSlug, locale, status, author_id }) {
|
||||||
|
let baseUrl = `${this.url}/${portalSlug}/articles?page=${pageNumber}&locale=${locale}`;
|
||||||
|
if (status !== undefined) baseUrl += `&status=${status}`;
|
||||||
|
if (author_id) baseUrl += `&author_id=${author_id}`;
|
||||||
|
return axios.get(baseUrl);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new ArticlesAPI();
|
export default new ArticlesAPI();
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
/* global axios */
|
|
||||||
import ApiClient from '../ApiClient';
|
import ApiClient from '../ApiClient';
|
||||||
|
|
||||||
class PortalsAPI extends ApiClient {
|
class PortalsAPI extends ApiClient {
|
||||||
constructor() {
|
constructor() {
|
||||||
super('portals', { accountScoped: true });
|
super('portals', { accountScoped: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
getArticles({ pageNumber, portalSlug, locale }) {
|
|
||||||
return axios.get(
|
|
||||||
`${this.url}/${portalSlug}/articles?page=${pageNumber}&locale=${locale}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default PortalsAPI;
|
export default PortalsAPI;
|
||||||
|
|
29
app/javascript/dashboard/api/specs/article.spec.js
Normal file
29
app/javascript/dashboard/api/specs/article.spec.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import articlesAPI from '../helpCenter/articles';
|
||||||
|
import ApiClient from 'dashboard/api/helpCenter/portals';
|
||||||
|
import describeWithAPIMock from './apiSpecHelper';
|
||||||
|
|
||||||
|
describe('#PortalAPI', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(articlesAPI).toBeInstanceOf(ApiClient);
|
||||||
|
expect(articlesAPI).toHaveProperty('get');
|
||||||
|
expect(articlesAPI).toHaveProperty('show');
|
||||||
|
expect(articlesAPI).toHaveProperty('create');
|
||||||
|
expect(articlesAPI).toHaveProperty('update');
|
||||||
|
expect(articlesAPI).toHaveProperty('delete');
|
||||||
|
expect(articlesAPI).toHaveProperty('getArticles');
|
||||||
|
});
|
||||||
|
describeWithAPIMock('API calls', context => {
|
||||||
|
it('#getArticles', () => {
|
||||||
|
articlesAPI.getArticles({
|
||||||
|
pageNumber: 1,
|
||||||
|
portalSlug: 'room-rental',
|
||||||
|
locale: 'en-US',
|
||||||
|
status: 'published',
|
||||||
|
author_id: '1',
|
||||||
|
});
|
||||||
|
expect(context.axiosMock.get).toHaveBeenCalledWith(
|
||||||
|
'/api/v1/portals/room-rental/articles?page=1&locale=en-US&status=published&author_id=1'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
13
app/javascript/dashboard/api/specs/portals.spec.js
Normal file
13
app/javascript/dashboard/api/specs/portals.spec.js
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import PortalsAPI from '../helpCenter/portals';
|
||||||
|
import ApiClient from '../ApiClient';
|
||||||
|
const portalAPI = new PortalsAPI();
|
||||||
|
describe('#PortalAPI', () => {
|
||||||
|
it('creates correct instance', () => {
|
||||||
|
expect(portalAPI).toBeInstanceOf(ApiClient);
|
||||||
|
expect(portalAPI).toHaveProperty('get');
|
||||||
|
expect(portalAPI).toHaveProperty('show');
|
||||||
|
expect(portalAPI).toHaveProperty('create');
|
||||||
|
expect(portalAPI).toHaveProperty('update');
|
||||||
|
expect(portalAPI).toHaveProperty('delete');
|
||||||
|
});
|
||||||
|
});
|
|
@ -31,7 +31,9 @@
|
||||||
"NEW_BUTTON": "New Portal",
|
"NEW_BUTTON": "New Portal",
|
||||||
"ACTIVE_BADGE": "active",
|
"ACTIVE_BADGE": "active",
|
||||||
"CHOOSE_LOCALE_LABEL": "Choose a locale",
|
"CHOOSE_LOCALE_LABEL": "Choose a locale",
|
||||||
|
"LOADING_MESSAGE": "Loading portals...",
|
||||||
"ARTICLES_LABEL": "articles",
|
"ARTICLES_LABEL": "articles",
|
||||||
|
"NO_PORTALS_MESSAGE": "There are no available portals",
|
||||||
"ADD_NEW_LOCALE": "Add a new locale",
|
"ADD_NEW_LOCALE": "Add a new locale",
|
||||||
"POPOVER": {
|
"POPOVER": {
|
||||||
"TITLE": "Portals",
|
"TITLE": "Portals",
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ category }}</td>
|
<td>{{ category.name }}</td>
|
||||||
<td>{{ readCount }}</td>
|
<td>{{ readCount }}</td>
|
||||||
<td>
|
<td>
|
||||||
<Label :title="status" :color-scheme="labelColor" />
|
<Label :title="status" :color-scheme="labelColor" />
|
||||||
|
@ -48,8 +48,8 @@ export default {
|
||||||
default: () => {},
|
default: () => {},
|
||||||
},
|
},
|
||||||
category: {
|
category: {
|
||||||
type: String,
|
type: Object,
|
||||||
default: '',
|
default: () => {},
|
||||||
},
|
},
|
||||||
readCount: {
|
readCount: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<table-footer
|
<table-footer
|
||||||
|
v-if="articles.length"
|
||||||
:on-page-change="onPageChange"
|
:on-page-change="onPageChange"
|
||||||
:current-page="Number(currentPage)"
|
:current-page="Number(currentPage)"
|
||||||
:total-count="totalCount"
|
:total-count="totalCount"
|
||||||
|
|
|
@ -6,10 +6,10 @@
|
||||||
@open-key-shortcut-modal="toggleKeyShortcutModal"
|
@open-key-shortcut-modal="toggleKeyShortcutModal"
|
||||||
@close-key-shortcut-modal="closeKeyShortcutModal"
|
@close-key-shortcut-modal="closeKeyShortcutModal"
|
||||||
/>
|
/>
|
||||||
<div class="margin-right-small">
|
<div v-if="portals.length" class="margin-right-small">
|
||||||
<help-center-sidebar
|
<help-center-sidebar
|
||||||
header-title="Help Center"
|
:header-title="headerTitle"
|
||||||
sub-title="English"
|
:sub-title="localeName(selectedPortalLocale)"
|
||||||
:accessible-menu-items="accessibleMenuItems"
|
:accessible-menu-items="accessibleMenuItems"
|
||||||
:additional-secondary-menu-items="additionalSecondaryMenuItems"
|
:additional-secondary-menu-items="additionalSecondaryMenuItems"
|
||||||
@open-popover="openPortalPopover"
|
@open-popover="openPortalPopover"
|
||||||
|
@ -30,8 +30,8 @@
|
||||||
<portal-popover
|
<portal-popover
|
||||||
v-if="showPortalPopover"
|
v-if="showPortalPopover"
|
||||||
:portals="portals"
|
:portals="portals"
|
||||||
|
:active-portal="selectedPortal"
|
||||||
@close-popover="closePortalPopover"
|
@close-popover="closePortalPopover"
|
||||||
@open-portal-page="openPortalPage"
|
|
||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
@ -41,12 +41,12 @@ import { mapGetters } from 'vuex';
|
||||||
|
|
||||||
import { frontendURL } from '../../../../helper/URLHelper';
|
import { frontendURL } from '../../../../helper/URLHelper';
|
||||||
import Sidebar from 'dashboard/components/layout/Sidebar';
|
import Sidebar from 'dashboard/components/layout/Sidebar';
|
||||||
import PortalPopover from 'dashboard/routes/dashboard/helpcenter/components/PortalPopover';
|
import PortalPopover from '../components/PortalPopover.vue';
|
||||||
import HelpCenterSidebar from 'dashboard/routes/dashboard/helpcenter/components/Sidebar/Sidebar';
|
import HelpCenterSidebar from '../components/Sidebar/Sidebar.vue';
|
||||||
import CommandBar from 'dashboard/routes/dashboard/commands/commandbar.vue';
|
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.vue';
|
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
||||||
|
import portalMixin from '../mixins/portalMixin';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Sidebar,
|
Sidebar,
|
||||||
|
@ -56,6 +56,7 @@ export default {
|
||||||
NotificationPanel,
|
NotificationPanel,
|
||||||
PortalPopover,
|
PortalPopover,
|
||||||
},
|
},
|
||||||
|
mixins: [portalMixin],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showShortcutModal: false,
|
showShortcutModal: false,
|
||||||
|
@ -63,36 +64,49 @@ export default {
|
||||||
showPortalPopover: false,
|
showPortalPopover: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({
|
...mapGetters({
|
||||||
accountId: 'getCurrentAccountId',
|
accountId: 'getCurrentAccountId',
|
||||||
|
selectedPortal: 'portals/getSelectedPortal',
|
||||||
|
portals: 'portals/allPortals',
|
||||||
|
meta: 'portals/getMeta',
|
||||||
|
isFetching: 'portals/isFetchingPortals',
|
||||||
}),
|
}),
|
||||||
portalSlug() {
|
selectedPortalSlug() {
|
||||||
return this.$route.params.portalSlug;
|
return this.portalSlug || this.selectedPortal?.slug;
|
||||||
},
|
},
|
||||||
locale() {
|
selectedPortalLocale() {
|
||||||
return this.$route.params.locale;
|
return this.locale || this.selectedPortal?.meta?.default_locale;
|
||||||
},
|
},
|
||||||
accessibleMenuItems() {
|
accessibleMenuItems() {
|
||||||
|
const {
|
||||||
|
meta: {
|
||||||
|
all_articles_count: allArticlesCount,
|
||||||
|
mine_articles_count: mineArticlesCount,
|
||||||
|
draft_articles_count: draftArticlesCount,
|
||||||
|
archived_articles_count: archivedArticlesCount,
|
||||||
|
} = {},
|
||||||
|
} = this.selectedPortal;
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
icon: 'book',
|
icon: 'book',
|
||||||
label: 'HELP_CENTER.ALL_ARTICLES',
|
label: 'HELP_CENTER.ALL_ARTICLES',
|
||||||
key: 'list_all_locale_articles',
|
key: 'list_all_locale_articles',
|
||||||
count: 199,
|
count: allArticlesCount,
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles`
|
`accounts/${this.accountId}/portals/${this.selectedPortalSlug}/${this.selectedPortalLocale}/articles`
|
||||||
),
|
),
|
||||||
toolTip: 'All Articles',
|
toolTip: 'All Articles',
|
||||||
toStateName: 'list_all_locale_articles',
|
toStateName: 'list_all_selectedPortalLocale_articles',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: 'pen',
|
icon: 'pen',
|
||||||
label: 'HELP_CENTER.MY_ARTICLES',
|
label: 'HELP_CENTER.MY_ARTICLES',
|
||||||
key: 'mine_articles',
|
key: 'mine_articles',
|
||||||
count: 112,
|
count: mineArticlesCount,
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/mine`
|
`accounts/${this.accountId}/portals/${this.selectedPortalSlug}/${this.selectedPortalLocale}/articles/mine`
|
||||||
),
|
),
|
||||||
toolTip: 'My articles',
|
toolTip: 'My articles',
|
||||||
toStateName: 'mine_articles',
|
toStateName: 'mine_articles',
|
||||||
|
@ -101,9 +115,9 @@ export default {
|
||||||
icon: 'draft',
|
icon: 'draft',
|
||||||
label: 'HELP_CENTER.DRAFT',
|
label: 'HELP_CENTER.DRAFT',
|
||||||
key: 'list_draft_articles',
|
key: 'list_draft_articles',
|
||||||
count: 32,
|
count: draftArticlesCount,
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/draft`
|
`accounts/${this.accountId}/portals/${this.selectedPortalSlug}/${this.selectedPortalLocale}/articles/draft`
|
||||||
),
|
),
|
||||||
toolTip: 'Draft',
|
toolTip: 'Draft',
|
||||||
toStateName: 'list_draft_articles',
|
toStateName: 'list_draft_articles',
|
||||||
|
@ -112,9 +126,9 @@ export default {
|
||||||
icon: 'archive',
|
icon: 'archive',
|
||||||
label: 'HELP_CENTER.ARCHIVED',
|
label: 'HELP_CENTER.ARCHIVED',
|
||||||
key: 'list_archived_articles',
|
key: 'list_archived_articles',
|
||||||
count: 10,
|
count: archivedArticlesCount,
|
||||||
toState: frontendURL(
|
toState: frontendURL(
|
||||||
`accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/archived`
|
`accounts/${this.accountId}/portals/${this.selectedPortalSlug}/${this.selectedPortalLocale}/articles/archived`
|
||||||
),
|
),
|
||||||
toolTip: 'Archived',
|
toolTip: 'Archived',
|
||||||
toStateName: 'list_archived_articles',
|
toStateName: 'list_archived_articles',
|
||||||
|
@ -147,172 +161,6 @@ export default {
|
||||||
`accounts/${this.accountId}/portals/:portalSlug/:locale/categories/channel`
|
`accounts/${this.accountId}/portals/:portalSlug/:locale/categories/channel`
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
label: 'Feature',
|
|
||||||
count: 24,
|
|
||||||
truncateLabel: true,
|
|
||||||
toState: frontendURL(
|
|
||||||
`accounts/${this.accountId}/portals/:portalSlug/:locale/categories/feature`
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
label: 'Advanced',
|
|
||||||
count: 8,
|
|
||||||
truncateLabel: true,
|
|
||||||
toState: frontendURL(
|
|
||||||
`accounts/${this.accountId}/portals/:portalSlug/:locale/categories/advanced`
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
label: 'Mobile app',
|
|
||||||
count: 3,
|
|
||||||
truncateLabel: true,
|
|
||||||
toState: frontendURL(
|
|
||||||
`accounts/${this.accountId}/portals/:portalSlug/:locale/categories/mobile-app`
|
|
||||||
),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
label: 'Others',
|
|
||||||
count: 39,
|
|
||||||
truncateLabel: true,
|
|
||||||
toState: frontendURL(
|
|
||||||
`accounts/${this.accountId}/portals/:portalSlug/:locale/categories/others`
|
|
||||||
),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
},
|
|
||||||
portals() {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
name: 'Chatwoot Help Center',
|
|
||||||
id: 1,
|
|
||||||
color: null,
|
|
||||||
custom_domain: 'doc',
|
|
||||||
articles_count: 123,
|
|
||||||
header_text: null,
|
|
||||||
homepage_link: null,
|
|
||||||
page_title: null,
|
|
||||||
slug: 'first_portal',
|
|
||||||
archived: false,
|
|
||||||
config: {
|
|
||||||
allowed_locales: [
|
|
||||||
{
|
|
||||||
code: 'en',
|
|
||||||
name: 'English',
|
|
||||||
articles_count: 123,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'fr',
|
|
||||||
name: 'Français',
|
|
||||||
articles_count: 123,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'de',
|
|
||||||
name: 'Deutsch',
|
|
||||||
articles_count: 32,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'es',
|
|
||||||
name: 'Español',
|
|
||||||
articles_count: 12,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'it',
|
|
||||||
name: 'Italiano',
|
|
||||||
articles_count: 8,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
locales: [
|
|
||||||
{
|
|
||||||
name: 'English',
|
|
||||||
code: 'en',
|
|
||||||
articles_count: 12,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Español',
|
|
||||||
code: 'es',
|
|
||||||
articles_count: 42,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'French',
|
|
||||||
code: 'fr',
|
|
||||||
articles_count: 29,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Italian',
|
|
||||||
code: 'it',
|
|
||||||
articles_count: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'German',
|
|
||||||
code: 'de',
|
|
||||||
articles_count: 66,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Chatwoot Docs',
|
|
||||||
id: 2,
|
|
||||||
color: null,
|
|
||||||
custom_domain: 'doc',
|
|
||||||
articles_count: 124,
|
|
||||||
header_text: null,
|
|
||||||
homepage_link: null,
|
|
||||||
page_title: null,
|
|
||||||
slug: 'second_portal',
|
|
||||||
archived: false,
|
|
||||||
config: {
|
|
||||||
allowed_locales: [
|
|
||||||
{
|
|
||||||
code: 'en',
|
|
||||||
name: 'English',
|
|
||||||
articles_count: 123,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'fr',
|
|
||||||
name: 'Français',
|
|
||||||
articles_count: 123,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'de',
|
|
||||||
name: 'Deutsch',
|
|
||||||
articles_count: 32,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'es',
|
|
||||||
name: 'Español',
|
|
||||||
articles_count: 12,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'it',
|
|
||||||
name: 'Italiano',
|
|
||||||
articles_count: 8,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
locales: [
|
|
||||||
{
|
|
||||||
name: 'English',
|
|
||||||
code: 'en',
|
|
||||||
articles_count: 12,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Japanese',
|
|
||||||
code: 'jp',
|
|
||||||
articles_count: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Mandarin',
|
|
||||||
code: 'CH',
|
|
||||||
articles_count: 6,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -320,8 +168,17 @@ export default {
|
||||||
currentRoute() {
|
currentRoute() {
|
||||||
return ' ';
|
return ' ';
|
||||||
},
|
},
|
||||||
|
headerTitle() {
|
||||||
|
return this.selectedPortal.name;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.fetchPortals();
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
fetchPortals() {
|
||||||
|
this.$store.dispatch('portals/index');
|
||||||
|
},
|
||||||
toggleKeyShortcutModal() {
|
toggleKeyShortcutModal() {
|
||||||
this.showShortcutModal = true;
|
this.showShortcutModal = true;
|
||||||
},
|
},
|
||||||
|
@ -340,12 +197,6 @@ export default {
|
||||||
closePortalPopover() {
|
closePortalPopover() {
|
||||||
this.showPortalPopover = false;
|
this.showPortalPopover = false;
|
||||||
},
|
},
|
||||||
openPortalPage() {
|
|
||||||
this.$router.push({
|
|
||||||
name: 'list_all_portals',
|
|
||||||
});
|
|
||||||
this.showPortalPopover = false;
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div v-for="portal in portals" :key="portal.id" class="portal">
|
<div class="portal">
|
||||||
<thumbnail :username="portal.name" variant="square" />
|
<thumbnail :username="portal.name" variant="square" />
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<header>
|
<header>
|
||||||
|
@ -118,7 +118,6 @@
|
||||||
class="theme-color"
|
class="theme-color"
|
||||||
:style="{ background: portal.color }"
|
:style="{ background: portal.color }"
|
||||||
/>
|
/>
|
||||||
<span class="text-block-title">{{ portal.page_title }}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="configuration-item">
|
<div class="configuration-item">
|
||||||
|
@ -127,7 +126,7 @@
|
||||||
'HELP_CENTER.PORTAL.PORTAL_SETTINGS.LIST_ITEM.PORTAL_CONFIG.ITEMS.SUB_TEXT'
|
'HELP_CENTER.PORTAL.PORTAL_SETTINGS.LIST_ITEM.PORTAL_CONFIG.ITEMS.SUB_TEXT'
|
||||||
)
|
)
|
||||||
}}</label>
|
}}</label>
|
||||||
<span class="text-block-title">{{ portal.page_title }}</span>
|
<span class="text-block-title">{{ portal.header_text }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -141,8 +140,8 @@
|
||||||
}}
|
}}
|
||||||
</h2>
|
</h2>
|
||||||
<locale-item-table
|
<locale-item-table
|
||||||
:portals="portal"
|
:locales="locales"
|
||||||
:selected-locale-code="selectedLocaleCode"
|
:selected-locale-code="portal.meta.default_locale"
|
||||||
@swap="swapLocale"
|
@swap="swapLocale"
|
||||||
@delete="deleteLocale"
|
@delete="deleteLocale"
|
||||||
/>
|
/>
|
||||||
|
@ -163,33 +162,28 @@ export default {
|
||||||
LocaleItemTable,
|
LocaleItemTable,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
portals: {
|
portal: {
|
||||||
type: Array,
|
type: Object,
|
||||||
default: () => [],
|
default: () => {},
|
||||||
},
|
},
|
||||||
status: {
|
status: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
values: ['archived', 'draft', 'published'],
|
values: ['archived', 'draft', 'published'],
|
||||||
},
|
},
|
||||||
selectedLocaleCode: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
labelColor() {
|
labelColor() {
|
||||||
switch (this.status) {
|
switch (this.status) {
|
||||||
case 'archived':
|
case 'Archived':
|
||||||
return 'secondary';
|
|
||||||
case 'draft':
|
|
||||||
return 'warning';
|
return 'warning';
|
||||||
default:
|
default:
|
||||||
return 'success';
|
return 'success';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
defaultLocale(code) {
|
|
||||||
return code === this.selectedLocaleCode;
|
locales() {
|
||||||
|
return this.portal ? this.portal.config.allowed_locales : [];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
@ -37,9 +37,9 @@
|
||||||
<td colspan="100%" class="horizontal-line" />
|
<td colspan="100%" class="horizontal-line" />
|
||||||
</tr>
|
</tr>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="locale in portals.locales" :key="locale.code">
|
<tr v-for="locale in locales" :key="locale.code">
|
||||||
<td>
|
<td>
|
||||||
<span>{{ locale.name }}</span>
|
<span>{{ localeName(locale.code) }}</span>
|
||||||
<Label
|
<Label
|
||||||
v-if="locale.code === selectedLocaleCode"
|
v-if="locale.code === selectedLocaleCode"
|
||||||
:title="
|
:title="
|
||||||
|
@ -95,20 +95,23 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Label from 'dashboard/components/ui/Label';
|
import Label from 'dashboard/components/ui/Label';
|
||||||
|
import portalMixin from '../mixins/portalMixin';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
Label,
|
Label,
|
||||||
},
|
},
|
||||||
|
mixins: [portalMixin],
|
||||||
props: {
|
props: {
|
||||||
portals: {
|
locales: {
|
||||||
type: Object,
|
type: Array,
|
||||||
default: () => {},
|
default: () => [],
|
||||||
},
|
},
|
||||||
selectedLocaleCode: {
|
selectedLocaleCode: {
|
||||||
type: String,
|
type: String,
|
||||||
default: '',
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
swapLocale() {
|
swapLocale() {
|
||||||
this.$emit('swap');
|
this.$emit('swap');
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
color-scheme="secondary"
|
color-scheme="secondary"
|
||||||
icon="settings"
|
icon="settings"
|
||||||
size="small"
|
size="small"
|
||||||
@click="openPortalPage"
|
|
||||||
>
|
>
|
||||||
{{ $t('HELP_CENTER.PORTAL.POPOVER.PORTAL_SETTINGS') }}
|
{{ $t('HELP_CENTER.PORTAL.POPOVER.PORTAL_SETTINGS') }}
|
||||||
</woot-button>
|
</woot-button>
|
||||||
|
@ -24,6 +23,8 @@
|
||||||
v-for="portal in portals"
|
v-for="portal in portals"
|
||||||
:key="portal.id"
|
:key="portal.id"
|
||||||
:portal="portal"
|
:portal="portal"
|
||||||
|
:active="portal.id === activePortal.id"
|
||||||
|
@open-portal-page="openPortalPage"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
|
@ -50,13 +51,26 @@ export default {
|
||||||
type: Array,
|
type: Array,
|
||||||
default: () => [],
|
default: () => [],
|
||||||
},
|
},
|
||||||
|
activePortal: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({}),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
closePortalPopover() {
|
closePortalPopover() {
|
||||||
this.$emit('close-popover');
|
this.$emit('close-popover');
|
||||||
},
|
},
|
||||||
openPortalPage() {
|
openPortalPage({ slug, locale }) {
|
||||||
this.$emit('open-portal-page');
|
this.$emit('close-popover');
|
||||||
|
const portal = this.portals.find(p => p.slug === slug);
|
||||||
|
this.$store.dispatch('portals/setPortalId', portal.id);
|
||||||
|
this.$router.push({
|
||||||
|
name: 'list_all_locale_articles',
|
||||||
|
params: {
|
||||||
|
portalSlug: slug,
|
||||||
|
locale: locale,
|
||||||
|
},
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div>
|
<div>
|
||||||
<h2 class="portal-title">{{ portal.name }}</h2>
|
<h2 class="portal-title">{{ portal.name }}</h2>
|
||||||
<p class="portal-count">
|
<p class="portal-count">
|
||||||
{{ portal.articles_count }}
|
{{ articlesCount }}
|
||||||
{{ $t('HELP_CENTER.PORTAL.ARTICLES_LABEL') }}
|
{{ $t('HELP_CENTER.PORTAL.ARTICLES_LABEL') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
{{ $t('HELP_CENTER.PORTAL.CHOOSE_LOCALE_LABEL') }}
|
{{ $t('HELP_CENTER.PORTAL.CHOOSE_LOCALE_LABEL') }}
|
||||||
</h2>
|
</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="locale in portal.locales" :key="locale.code">
|
<li v-for="locale in locales" :key="locale.code">
|
||||||
<label :for="`locale-${locale.code}`" class="locale-item">
|
<label :for="`locale-${locale.code}`" class="locale-item">
|
||||||
<input
|
<input
|
||||||
:id="`locale-${locale.code}`"
|
:id="`locale-${locale.code}`"
|
||||||
|
@ -27,9 +27,10 @@
|
||||||
type="radio"
|
type="radio"
|
||||||
name="locale"
|
name="locale"
|
||||||
:value="locale.code"
|
:value="locale.code"
|
||||||
|
@click="onClick(locale.code, portal)"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<p>{{ locale.name }}</p>
|
<p>{{ localeName(locale.code) }}</p>
|
||||||
<span>
|
<span>
|
||||||
{{ locale.articles_count }}
|
{{ locale.articles_count }}
|
||||||
{{ $t('HELP_CENTER.PORTAL.ARTICLES_LABEL') }} -
|
{{ $t('HELP_CENTER.PORTAL.ARTICLES_LABEL') }} -
|
||||||
|
@ -49,10 +50,12 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import thumbnail from 'dashboard/components/widgets/Thumbnail';
|
import thumbnail from 'dashboard/components/widgets/Thumbnail';
|
||||||
|
import portalMixin from '../mixins/portalMixin';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
thumbnail,
|
thumbnail,
|
||||||
},
|
},
|
||||||
|
mixins: [portalMixin],
|
||||||
props: {
|
props: {
|
||||||
portal: {
|
portal: {
|
||||||
type: Object,
|
type: Object,
|
||||||
|
@ -65,11 +68,24 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedLocale: '',
|
selectedLocale: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
locales() {
|
||||||
|
return this.portal?.config?.allowed_locales;
|
||||||
|
},
|
||||||
|
articlesCount() {
|
||||||
|
return this.portal?.meta?.all_articles_count;
|
||||||
|
},
|
||||||
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.selectedLocale = this.portal.locales[0].code;
|
this.selectedLocale = this.locale || this.portal?.meta?.default_locale;
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onClick(code, portal) {
|
||||||
|
this.$emit('open-portal-page', { slug: portal.slug, locale: code });
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { frontendURL } from 'dashboard/helper/URLHelper';
|
import { frontendURL } from 'dashboard/helper/URLHelper';
|
||||||
|
import allLocales from 'shared/constants/locales.js';
|
||||||
export default {
|
export default {
|
||||||
computed: {
|
computed: {
|
||||||
...mapGetters({ accountId: 'getCurrentAccountId' }),
|
...mapGetters({ accountId: 'getCurrentAccountId' }),
|
||||||
|
@ -16,5 +17,8 @@ export default {
|
||||||
`accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/${id}`
|
`accounts/${this.accountId}/portals/${this.portalSlug}/${this.locale}/articles/${id}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
localeName(code) {
|
||||||
|
return allLocales[code];
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -40,7 +40,7 @@ describe('portalMixin', () => {
|
||||||
expect(wrapper.vm.accountId).toBe(1);
|
expect(wrapper.vm.accountId).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns portal url', () => {
|
it('returns article url', () => {
|
||||||
router.push({
|
router.push({
|
||||||
name: 'list_all_locale_articles',
|
name: 'list_all_locale_articles',
|
||||||
params: { portalSlug: 'fur-rent', locale: 'en' },
|
params: { portalSlug: 'fur-rent', locale: 'en' },
|
||||||
|
@ -65,4 +65,12 @@ describe('portalMixin', () => {
|
||||||
});
|
});
|
||||||
expect(wrapper.vm.portalSlug).toBe('campaign');
|
expect(wrapper.vm.portalSlug).toBe('campaign');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('returns locale name', () => {
|
||||||
|
router.push({
|
||||||
|
name: 'list_all_locale_articles',
|
||||||
|
params: { portalSlug: 'fur-rent', locale: 'es' },
|
||||||
|
});
|
||||||
|
expect(wrapper.vm.localeName('es')).toBe('Spanish');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -13,12 +13,12 @@
|
||||||
:total-count="meta.count"
|
:total-count="meta.count"
|
||||||
@on-page-change="onPageChange"
|
@on-page-change="onPageChange"
|
||||||
/>
|
/>
|
||||||
<div v-if="isFetching" class="articles--loader">
|
<div v-if="shouldShowLoader" class="articles--loader">
|
||||||
<spinner />
|
<spinner />
|
||||||
<span>{{ $t('HELP_CENTER.TABLE.LOADING_MESSAGE') }}</span>
|
<span>{{ $t('HELP_CENTER.TABLE.LOADING_MESSAGE') }}</span>
|
||||||
</div>
|
</div>
|
||||||
<empty-state
|
<empty-state
|
||||||
v-else-if="!isFetching && !articles.length"
|
v-else-if="shouldShowEmptyState"
|
||||||
:title="$t('HELP_CENTER.TABLE.NO_ARTICLES')"
|
:title="$t('HELP_CENTER.TABLE.NO_ARTICLES')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,10 +48,13 @@ export default {
|
||||||
uiFlags: 'articles/uiFlags',
|
uiFlags: 'articles/uiFlags',
|
||||||
meta: 'articles/getMeta',
|
meta: 'articles/getMeta',
|
||||||
isFetching: 'articles/isFetching',
|
isFetching: 'articles/isFetching',
|
||||||
|
currentUserId: 'getCurrentUserID',
|
||||||
}),
|
}),
|
||||||
|
shouldShowEmptyState() {
|
||||||
showEmptyState() {
|
return !this.isFetching && !this.articles.length;
|
||||||
return this.articles.length === 0;
|
},
|
||||||
|
shouldShowLoader() {
|
||||||
|
return this.isFetching && !this.articles.length;
|
||||||
},
|
},
|
||||||
articleType() {
|
articleType() {
|
||||||
return this.$route.path.split('/').pop();
|
return this.$route.path.split('/').pop();
|
||||||
|
@ -68,19 +71,46 @@ export default {
|
||||||
return this.$t('HELP_CENTER.HEADER.TITLES.ALL_ARTICLES');
|
return this.$t('HELP_CENTER.HEADER.TITLES.ALL_ARTICLES');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
status() {
|
||||||
|
switch (this.articleType) {
|
||||||
|
case 'draft':
|
||||||
|
return 0;
|
||||||
|
case 'published':
|
||||||
|
return 1;
|
||||||
|
case 'archived':
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
author() {
|
||||||
|
if (this.articleType === 'mine') {
|
||||||
|
return this.currentUserId;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
$route() {
|
||||||
|
this.pageNumber = 1;
|
||||||
|
this.fetchArticles();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.fetchArticles({ pageNumber: this.pageNumber });
|
this.fetchArticles();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
newArticlePage() {
|
newArticlePage() {
|
||||||
this.$router.push({ name: 'new_article' });
|
this.$router.push({ name: 'new_article' });
|
||||||
},
|
},
|
||||||
fetchArticles({ pageNumber }) {
|
fetchArticles() {
|
||||||
this.$store.dispatch('articles/index', {
|
this.$store.dispatch('articles/index', {
|
||||||
pageNumber,
|
pageNumber: this.pageNumber,
|
||||||
portalSlug: this.$route.params.portalSlug,
|
portalSlug: this.$route.params.portalSlug,
|
||||||
locale: this.$route.params.locale,
|
locale: this.$route.params.locale,
|
||||||
|
status: this.status,
|
||||||
|
author_id: this.author,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onPageChange(page) {
|
onPageChange(page) {
|
||||||
|
|
|
@ -8,134 +8,46 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="portal-container">
|
<div class="portal-container">
|
||||||
<portal-list-item
|
<portal-list-item
|
||||||
:portals="portals"
|
v-for="portal in portals"
|
||||||
status="published"
|
:key="portal.id"
|
||||||
selected-locale-code="en-US"
|
:portal="portal"
|
||||||
|
:status="portalStatus"
|
||||||
|
/>
|
||||||
|
<div v-if="isFetching" class="portals--loader">
|
||||||
|
<spinner />
|
||||||
|
<span>{{ $t('HELP_CENTER.PORTAL.LOADING_MESSAGE') }}</span>
|
||||||
|
</div>
|
||||||
|
<empty-state
|
||||||
|
v-else-if="shouldShowEmptyState"
|
||||||
|
:title="$t('HELP_CENTER.PORTAL.NO_PORTALS_MESSAGE')"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import PortalListItem from 'dashboard/routes/dashboard/helpcenter/components/PortalListItem';
|
import { mapGetters } from 'vuex';
|
||||||
|
import PortalListItem from '../../components/PortalListItem';
|
||||||
|
import Spinner from 'shared/components/Spinner.vue';
|
||||||
|
import EmptyState from 'dashboard/components/widgets/EmptyState';
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
PortalListItem,
|
PortalListItem,
|
||||||
|
EmptyState,
|
||||||
|
Spinner,
|
||||||
},
|
},
|
||||||
data() {
|
computed: {
|
||||||
return {
|
...mapGetters({
|
||||||
// Dummy data for testing will remove once the state is implemented.
|
portals: 'portals/allPortals',
|
||||||
portals: [
|
meta: 'portals/getMeta',
|
||||||
{
|
isFetching: 'portals/isFetchingPortals',
|
||||||
name: 'Chatwoot Help Center',
|
}),
|
||||||
id: 1,
|
portalStatus() {
|
||||||
color: 'red',
|
return this.archived ? 'Archived' : 'Live';
|
||||||
custom_domain: 'help-center.chatwoot.com',
|
|
||||||
articles_count: 123,
|
|
||||||
header_text: 'Help center',
|
|
||||||
homepage_link: null,
|
|
||||||
page_title: 'English',
|
|
||||||
slug: 'help-center',
|
|
||||||
archived: false,
|
|
||||||
config: {
|
|
||||||
allowed_locales: [
|
|
||||||
{
|
|
||||||
code: 'en-US',
|
|
||||||
name: 'English',
|
|
||||||
articles_count: 123,
|
|
||||||
categories_count: 42,
|
|
||||||
},
|
},
|
||||||
{
|
shouldShowEmptyState() {
|
||||||
code: 'fr-FR',
|
return !this.isFetching && !this.portals.length;
|
||||||
name: 'Français',
|
|
||||||
articles_count: 23,
|
|
||||||
categories_count: 11,
|
|
||||||
},
|
},
|
||||||
{
|
|
||||||
code: 'de-DE',
|
|
||||||
name: 'Deutsch',
|
|
||||||
articles_count: 32,
|
|
||||||
categories_count: 12,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'es-ES',
|
|
||||||
name: 'Español',
|
|
||||||
articles_count: 12,
|
|
||||||
categories_count: 4,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
locales: [
|
|
||||||
{
|
|
||||||
code: 'en-US',
|
|
||||||
name: 'English',
|
|
||||||
articles_count: 123,
|
|
||||||
categories_count: 42,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'fr-FR',
|
|
||||||
name: 'Français',
|
|
||||||
articles_count: 23,
|
|
||||||
categories_count: 11,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'de-DE',
|
|
||||||
name: 'Deutsch',
|
|
||||||
articles_count: 32,
|
|
||||||
categories_count: 12,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
code: 'es-ES',
|
|
||||||
name: 'Español',
|
|
||||||
articles_count: 12,
|
|
||||||
categories_count: 4,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Chatwoot Docs',
|
|
||||||
id: 2,
|
|
||||||
color: 'green',
|
|
||||||
custom_domain: 'doc-chatwoot.com',
|
|
||||||
articles_count: 67,
|
|
||||||
header_text: 'Docs',
|
|
||||||
homepage_link: null,
|
|
||||||
page_title: 'Portal',
|
|
||||||
slug: 'second_portal',
|
|
||||||
archived: false,
|
|
||||||
config: {
|
|
||||||
allowed_locales: [
|
|
||||||
{
|
|
||||||
name: 'English',
|
|
||||||
code: 'en-EN',
|
|
||||||
articles_count: 12,
|
|
||||||
categories_count: 66,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Mandarin',
|
|
||||||
code: 'ch-CH',
|
|
||||||
articles_count: 6,
|
|
||||||
categories_count: 23,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
locales: [
|
|
||||||
{
|
|
||||||
name: 'English',
|
|
||||||
code: 'en-EN',
|
|
||||||
articles_count: 12,
|
|
||||||
categories_count: 66,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Mandarin',
|
|
||||||
code: 'ch-CH',
|
|
||||||
articles_count: 6,
|
|
||||||
categories_count: 23,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
createPortal() {
|
createPortal() {
|
||||||
|
@ -149,7 +61,13 @@ export default {
|
||||||
.container {
|
.container {
|
||||||
padding: var(--space-small) var(--space-normal);
|
padding: var(--space-small) var(--space-normal);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
.portals--loader {
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
font-size: var(--font-size-default);
|
||||||
|
justify-content: center;
|
||||||
|
padding: var(--space-big);
|
||||||
|
}
|
||||||
.header-wrap {
|
.header-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
|
|
@ -36,6 +36,8 @@ import teams from './modules/teams';
|
||||||
import userNotificationSettings from './modules/userNotificationSettings';
|
import userNotificationSettings from './modules/userNotificationSettings';
|
||||||
import webhooks from './modules/webhooks';
|
import webhooks from './modules/webhooks';
|
||||||
import articles from './modules/helpCenterArticles';
|
import articles from './modules/helpCenterArticles';
|
||||||
|
import portals from './modules/helpCenterPortals';
|
||||||
|
import categories from './modules/helpCenterCategories';
|
||||||
|
|
||||||
Vue.use(Vuex);
|
Vue.use(Vuex);
|
||||||
export default new Vuex.Store({
|
export default new Vuex.Store({
|
||||||
|
@ -75,5 +77,7 @@ export default new Vuex.Store({
|
||||||
userNotificationSettings,
|
userNotificationSettings,
|
||||||
webhooks,
|
webhooks,
|
||||||
articles,
|
articles,
|
||||||
|
portals,
|
||||||
|
categories,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,18 +1,22 @@
|
||||||
import PortalAPI from 'dashboard/api/helpCenter/portals';
|
|
||||||
import articlesAPI from 'dashboard/api/helpCenter/articles';
|
import articlesAPI from 'dashboard/api/helpCenter/articles';
|
||||||
import { throwErrorMessage } from 'dashboard/store/utils/api';
|
import { throwErrorMessage } from 'dashboard/store/utils/api';
|
||||||
const portalAPIs = new PortalAPI();
|
|
||||||
import types from '../../mutation-types';
|
import types from '../../mutation-types';
|
||||||
export const actions = {
|
export const actions = {
|
||||||
index: async ({ commit }, { pageNumber, portalSlug, locale }) => {
|
index: async (
|
||||||
|
{ commit },
|
||||||
|
{ pageNumber, portalSlug, locale, status, author_id }
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
commit(types.SET_UI_FLAG, { isFetching: true });
|
commit(types.SET_UI_FLAG, { isFetching: true });
|
||||||
const {
|
const {
|
||||||
data: { payload, meta },
|
data: { payload, meta },
|
||||||
} = await portalAPIs.getArticles({
|
} = await articlesAPI.getArticles({
|
||||||
pageNumber,
|
pageNumber,
|
||||||
portalSlug,
|
portalSlug,
|
||||||
locale,
|
locale,
|
||||||
|
status,
|
||||||
|
author_id,
|
||||||
});
|
});
|
||||||
const articleIds = payload.map(article => article.id);
|
const articleIds = payload.map(article => article.id);
|
||||||
commit(types.CLEAR_ARTICLES);
|
commit(types.CLEAR_ARTICLES);
|
||||||
|
|
|
@ -8,7 +8,7 @@ export const defaultHelpCenterFlags = {
|
||||||
isDeleting: false,
|
isDeleting: false,
|
||||||
};
|
};
|
||||||
const state = {
|
const state = {
|
||||||
categoriess: {
|
categories: {
|
||||||
byId: {},
|
byId: {},
|
||||||
byLocale: {},
|
byLocale: {},
|
||||||
allIds: [],
|
allIds: [],
|
||||||
|
|
|
@ -3,13 +3,22 @@ import { throwErrorMessage } from 'dashboard/store/utils/api';
|
||||||
import { types } from './mutations';
|
import { types } from './mutations';
|
||||||
const portalAPIs = new PortalAPI();
|
const portalAPIs = new PortalAPI();
|
||||||
export const actions = {
|
export const actions = {
|
||||||
index: async ({ commit }) => {
|
index: async ({ commit, state, dispatch }) => {
|
||||||
try {
|
try {
|
||||||
commit(types.SET_UI_FLAG, { isFetching: true });
|
commit(types.SET_UI_FLAG, { isFetching: true });
|
||||||
const { data } = await portalAPIs.get();
|
const {
|
||||||
const portalIds = data.map(portal => portal.id);
|
data: { payload, meta },
|
||||||
commit(types.ADD_MANY_PORTALS_ENTRY, data);
|
} = await portalAPIs.get();
|
||||||
|
commit(types.CLEAR_PORTALS);
|
||||||
|
const portalIds = payload.map(portal => portal.id);
|
||||||
|
commit(types.ADD_MANY_PORTALS_ENTRY, payload);
|
||||||
commit(types.ADD_MANY_PORTALS_IDS, portalIds);
|
commit(types.ADD_MANY_PORTALS_IDS, portalIds);
|
||||||
|
const { selectedPortalId } = state;
|
||||||
|
// Check if selected portal is still in the portals list
|
||||||
|
if (!portalIds.includes(selectedPortalId)) {
|
||||||
|
dispatch('setPortalId', portalIds[0]);
|
||||||
|
}
|
||||||
|
commit(types.SET_PORTALS_META, meta);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throwErrorMessage(error);
|
throwErrorMessage(error);
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -68,4 +77,8 @@ export const actions = {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setPortalId: async ({ commit }, portalId) => {
|
||||||
|
commit(types.SET_SELECTED_PORTAL_ID, portalId);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,11 +16,18 @@ export const getters = {
|
||||||
},
|
},
|
||||||
allPortals: (...getterArguments) => {
|
allPortals: (...getterArguments) => {
|
||||||
const [state, _getters] = getterArguments;
|
const [state, _getters] = getterArguments;
|
||||||
|
|
||||||
const portals = state.portals.allIds.map(id => {
|
const portals = state.portals.allIds.map(id => {
|
||||||
return _getters.portalById(id);
|
return _getters.portalById(id);
|
||||||
});
|
});
|
||||||
return portals;
|
return portals;
|
||||||
},
|
},
|
||||||
count: state => state.portals.allIds.length || 0,
|
count: state => state.portals.allIds.length || 0,
|
||||||
|
getMeta: state => {
|
||||||
|
return state.meta;
|
||||||
|
},
|
||||||
|
getSelectedPortal: (...getterArguments) => {
|
||||||
|
const [state, _getters] = getterArguments;
|
||||||
|
const { selectedPortalId } = state.portals;
|
||||||
|
return _getters.portalById(selectedPortalId);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,11 @@ export const defaultPortalFlags = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
|
meta: {
|
||||||
|
count: 0,
|
||||||
|
currentPage: 1,
|
||||||
|
},
|
||||||
|
|
||||||
portals: {
|
portals: {
|
||||||
byId: {},
|
byId: {},
|
||||||
allIds: [],
|
allIds: [],
|
||||||
|
@ -20,6 +25,7 @@ const state = {
|
||||||
meta: {
|
meta: {
|
||||||
byId: {},
|
byId: {},
|
||||||
},
|
},
|
||||||
|
selectedPortalId: null,
|
||||||
},
|
},
|
||||||
uiFlags: {
|
uiFlags: {
|
||||||
allFetched: false,
|
allFetched: false,
|
||||||
|
|
|
@ -4,9 +4,12 @@ import { defaultPortalFlags } from './index';
|
||||||
export const types = {
|
export const types = {
|
||||||
SET_UI_FLAG: 'setUIFlag',
|
SET_UI_FLAG: 'setUIFlag',
|
||||||
ADD_PORTAL_ENTRY: 'addPortalEntry',
|
ADD_PORTAL_ENTRY: 'addPortalEntry',
|
||||||
|
SET_PORTALS_META: 'setPortalsMeta',
|
||||||
ADD_MANY_PORTALS_ENTRY: 'addManyPortalsEntry',
|
ADD_MANY_PORTALS_ENTRY: 'addManyPortalsEntry',
|
||||||
ADD_PORTAL_ID: 'addPortalId',
|
ADD_PORTAL_ID: 'addPortalId',
|
||||||
|
CLEAR_PORTALS: 'clearPortals',
|
||||||
ADD_MANY_PORTALS_IDS: 'addManyPortalsIds',
|
ADD_MANY_PORTALS_IDS: 'addManyPortalsIds',
|
||||||
|
SET_SELECTED_PORTAL_ID: 'setSelectedPortalId',
|
||||||
UPDATE_PORTAL_ENTRY: 'updatePortalEntry',
|
UPDATE_PORTAL_ENTRY: 'updatePortalEntry',
|
||||||
REMOVE_PORTAL_ENTRY: 'removePortalEntry',
|
REMOVE_PORTAL_ENTRY: 'removePortalEntry',
|
||||||
REMOVE_PORTAL_ID: 'removePortalId',
|
REMOVE_PORTAL_ID: 'removePortalId',
|
||||||
|
@ -32,9 +35,23 @@ export const mutations = {
|
||||||
portals.forEach(portal => {
|
portals.forEach(portal => {
|
||||||
allPortals[portal.id] = portal;
|
allPortals[portal.id] = portal;
|
||||||
});
|
});
|
||||||
Vue.set($state.portals, 'byId', {
|
Vue.set($state.portals, 'byId', allPortals);
|
||||||
allPortals,
|
},
|
||||||
});
|
|
||||||
|
[types.CLEAR_PORTALS]: $state => {
|
||||||
|
Vue.set($state.portals, 'byId', {});
|
||||||
|
Vue.set($state.portals, 'allIds', []);
|
||||||
|
Vue.set($state.portals, 'uiFlags', {});
|
||||||
|
},
|
||||||
|
|
||||||
|
[types.SET_PORTALS_META]: ($state, data) => {
|
||||||
|
const { portals_count: count, current_page: currentPage } = data;
|
||||||
|
Vue.set($state.meta, 'count', count);
|
||||||
|
Vue.set($state.meta, 'currentPage', currentPage);
|
||||||
|
},
|
||||||
|
|
||||||
|
[types.SET_SELECTED_PORTAL_ID]: ($state, portalId) => {
|
||||||
|
Vue.set($state.portals, 'selectedPortalId', portalId);
|
||||||
},
|
},
|
||||||
|
|
||||||
[types.ADD_PORTAL_ID]($state, portalId) {
|
[types.ADD_PORTAL_ID]($state, portalId) {
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { types } from '../mutations';
|
||||||
import { apiResponse } from './fixtures';
|
import { apiResponse } from './fixtures';
|
||||||
|
|
||||||
const commit = jest.fn();
|
const commit = jest.fn();
|
||||||
|
const dispatch = jest.fn();
|
||||||
global.axios = axios;
|
global.axios = axios;
|
||||||
jest.mock('axios');
|
jest.mock('axios');
|
||||||
|
|
||||||
|
@ -11,11 +12,20 @@ describe('#actions', () => {
|
||||||
describe('#index', () => {
|
describe('#index', () => {
|
||||||
it('sends correct actions if API is success', async () => {
|
it('sends correct actions if API is success', async () => {
|
||||||
axios.get.mockResolvedValue({ data: apiResponse });
|
axios.get.mockResolvedValue({ data: apiResponse });
|
||||||
await actions.index({ commit });
|
await actions.index({
|
||||||
|
commit,
|
||||||
|
dispatch,
|
||||||
|
state: {
|
||||||
|
selectedPortalId: 4,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(dispatch.mock.calls).toMatchObject([['setPortalId', 1]]);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[types.SET_UI_FLAG, { isFetching: true }],
|
[types.SET_UI_FLAG, { isFetching: true }],
|
||||||
[types.ADD_MANY_PORTALS_ENTRY, apiResponse],
|
[types.CLEAR_PORTALS],
|
||||||
|
[types.ADD_MANY_PORTALS_ENTRY, apiResponse.payload],
|
||||||
[types.ADD_MANY_PORTALS_IDS, [1, 2]],
|
[types.ADD_MANY_PORTALS_IDS, [1, 2]],
|
||||||
|
[types.SET_PORTALS_META, { current_page: 1, portals_count: 1 }],
|
||||||
[types.SET_UI_FLAG, { isFetching: false }],
|
[types.SET_UI_FLAG, { isFetching: false }],
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -31,7 +41,7 @@ describe('#actions', () => {
|
||||||
|
|
||||||
describe('#create', () => {
|
describe('#create', () => {
|
||||||
it('sends correct actions if API is success', async () => {
|
it('sends correct actions if API is success', async () => {
|
||||||
axios.post.mockResolvedValue({ data: apiResponse[1] });
|
axios.post.mockResolvedValue({ data: apiResponse.payload[1] });
|
||||||
await actions.create(
|
await actions.create(
|
||||||
{ commit },
|
{ commit },
|
||||||
{
|
{
|
||||||
|
@ -42,7 +52,7 @@ describe('#actions', () => {
|
||||||
);
|
);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[types.SET_UI_FLAG, { isCreating: true }],
|
[types.SET_UI_FLAG, { isCreating: true }],
|
||||||
[types.ADD_PORTAL_ENTRY, apiResponse[1]],
|
[types.ADD_PORTAL_ENTRY, apiResponse.payload[1]],
|
||||||
[types.ADD_PORTAL_ID, 2],
|
[types.ADD_PORTAL_ID, 2],
|
||||||
[types.SET_UI_FLAG, { isCreating: false }],
|
[types.SET_UI_FLAG, { isCreating: false }],
|
||||||
]);
|
]);
|
||||||
|
@ -59,14 +69,14 @@ 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: apiResponse[1] });
|
axios.patch.mockResolvedValue({ data: apiResponse.payload[1] });
|
||||||
await actions.update({ commit }, apiResponse[1]);
|
await actions.update({ commit }, apiResponse.payload[1]);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[
|
[
|
||||||
types.SET_HELP_PORTAL_UI_FLAG,
|
types.SET_HELP_PORTAL_UI_FLAG,
|
||||||
{ uiFlags: { isUpdating: true }, portalId: 2 },
|
{ uiFlags: { isUpdating: true }, portalId: 2 },
|
||||||
],
|
],
|
||||||
[types.UPDATE_PORTAL_ENTRY, apiResponse[1]],
|
[types.UPDATE_PORTAL_ENTRY, apiResponse.payload[1]],
|
||||||
[
|
[
|
||||||
types.SET_HELP_PORTAL_UI_FLAG,
|
types.SET_HELP_PORTAL_UI_FLAG,
|
||||||
{ uiFlags: { isUpdating: false }, portalId: 2 },
|
{ uiFlags: { isUpdating: false }, portalId: 2 },
|
||||||
|
@ -75,9 +85,9 @@ 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(actions.update({ commit }, apiResponse[1])).rejects.toThrow(
|
await expect(
|
||||||
Error
|
actions.update({ commit }, apiResponse.payload[1])
|
||||||
);
|
).rejects.toThrow(Error);
|
||||||
expect(commit.mock.calls).toEqual([
|
expect(commit.mock.calls).toEqual([
|
||||||
[
|
[
|
||||||
types.SET_HELP_PORTAL_UI_FLAG,
|
types.SET_HELP_PORTAL_UI_FLAG,
|
||||||
|
@ -123,4 +133,11 @@ describe('#actions', () => {
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('#setPortalId', () => {
|
||||||
|
it('sends correct actions', async () => {
|
||||||
|
axios.delete.mockResolvedValue({});
|
||||||
|
await actions.setPortalId({ commit }, 1);
|
||||||
|
expect(commit.mock.calls).toEqual([[types.SET_SELECTED_PORTAL_ID, 1]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
export default {
|
export default {
|
||||||
|
meta: {
|
||||||
|
count: 0,
|
||||||
|
currentPage: 1,
|
||||||
|
},
|
||||||
portals: {
|
portals: {
|
||||||
byId: {
|
byId: {
|
||||||
1: {
|
1: {
|
||||||
|
@ -36,6 +40,7 @@ export default {
|
||||||
1: { isFetching: false, isUpdating: true, isDeleting: false },
|
1: { isFetching: false, isUpdating: true, isDeleting: false },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
selectedPortalId: 1,
|
||||||
},
|
},
|
||||||
uiFlags: {
|
uiFlags: {
|
||||||
allFetched: false,
|
allFetched: false,
|
||||||
|
@ -43,7 +48,8 @@ export default {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const apiResponse = [
|
export const apiResponse = {
|
||||||
|
payload: [
|
||||||
{
|
{
|
||||||
id: 1,
|
id: 1,
|
||||||
color: 'red',
|
color: 'red',
|
||||||
|
@ -72,4 +78,9 @@ export const apiResponse = [
|
||||||
allowed_locales: ['en'],
|
allowed_locales: ['en'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
],
|
||||||
|
meta: {
|
||||||
|
current_page: 1,
|
||||||
|
portals_count: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
|
@ -42,4 +42,9 @@ describe('#getters', () => {
|
||||||
const state = portal;
|
const state = portal;
|
||||||
expect(getters.count(state)).toEqual(2);
|
expect(getters.count(state)).toEqual(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('getMeta', () => {
|
||||||
|
const state = portal;
|
||||||
|
expect(getters.getMeta(state)).toEqual({ count: 0, currentPage: 1 });
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -92,4 +92,33 @@ describe('#mutations', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#CLEAR_PORTALS', () => {
|
||||||
|
it('clears portals', () => {
|
||||||
|
mutations[types.CLEAR_PORTALS](state);
|
||||||
|
expect(state.portals.allIds).toEqual([]);
|
||||||
|
expect(state.portals.byId).toEqual({});
|
||||||
|
expect(state.portals.uiFlags).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#SET_PORTALS_META', () => {
|
||||||
|
it('add meta to state', () => {
|
||||||
|
mutations[types.SET_PORTALS_META](state, {
|
||||||
|
portals_count: 10,
|
||||||
|
current_page: 1,
|
||||||
|
});
|
||||||
|
expect(state.meta).toEqual({
|
||||||
|
count: 10,
|
||||||
|
currentPage: 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#SET_SELECTED_PORTAL_ID', () => {
|
||||||
|
it('set selected portal id', () => {
|
||||||
|
mutations[types.SET_SELECTED_PORTAL_ID](state, 4);
|
||||||
|
expect(state.portals.selectedPortalId).toEqual(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
566
app/javascript/shared/constants/locales.js
Normal file
566
app/javascript/shared/constants/locales.js
Normal file
|
@ -0,0 +1,566 @@
|
||||||
|
const locales = {
|
||||||
|
af: 'Afrikaans',
|
||||||
|
af_NA: 'Afrikaans (Namibia)',
|
||||||
|
af_ZA: 'Afrikaans (South Africa)',
|
||||||
|
ak: 'Akan',
|
||||||
|
ak_GH: 'Akan (Ghana)',
|
||||||
|
sq: 'Albanian',
|
||||||
|
sq_AL: 'Albanian (Albania)',
|
||||||
|
sq_XK: 'Albanian (Kosovo)',
|
||||||
|
sq_MK: 'Albanian (Macedonia)',
|
||||||
|
am: 'Amharic',
|
||||||
|
am_ET: 'Amharic (Ethiopia)',
|
||||||
|
ar: 'Arabic',
|
||||||
|
ar_DZ: 'Arabic (Algeria)',
|
||||||
|
ar_BH: 'Arabic (Bahrain)',
|
||||||
|
ar_TD: 'Arabic (Chad)',
|
||||||
|
ar_KM: 'Arabic (Comoros)',
|
||||||
|
ar_DJ: 'Arabic (Djibouti)',
|
||||||
|
ar_EG: 'Arabic (Egypt)',
|
||||||
|
ar_ER: 'Arabic (Eritrea)',
|
||||||
|
ar_IQ: 'Arabic (Iraq)',
|
||||||
|
ar_IL: 'Arabic (Israel)',
|
||||||
|
ar_JO: 'Arabic (Jordan)',
|
||||||
|
ar_KW: 'Arabic (Kuwait)',
|
||||||
|
ar_LB: 'Arabic (Lebanon)',
|
||||||
|
ar_LY: 'Arabic (Libya)',
|
||||||
|
ar_MR: 'Arabic (Mauritania)',
|
||||||
|
ar_MA: 'Arabic (Morocco)',
|
||||||
|
ar_OM: 'Arabic (Oman)',
|
||||||
|
ar_PS: 'Arabic (Palestinian Territories)',
|
||||||
|
ar_QA: 'Arabic (Qatar)',
|
||||||
|
ar_SA: 'Arabic (Saudi Arabia)',
|
||||||
|
ar_SO: 'Arabic (Somalia)',
|
||||||
|
ar_SS: 'Arabic (South Sudan)',
|
||||||
|
ar_SD: 'Arabic (Sudan)',
|
||||||
|
ar_SY: 'Arabic (Syria)',
|
||||||
|
ar_TN: 'Arabic (Tunisia)',
|
||||||
|
ar_AE: 'Arabic (United Arab Emirates)',
|
||||||
|
ar_EH: 'Arabic (Western Sahara)',
|
||||||
|
ar_YE: 'Arabic (Yemen)',
|
||||||
|
hy: 'Armenian',
|
||||||
|
hy_AM: 'Armenian (Armenia)',
|
||||||
|
as: 'Assamese',
|
||||||
|
as_IN: 'Assamese (India)',
|
||||||
|
az: 'Azerbaijani',
|
||||||
|
az_AZ: 'Azerbaijani (Azerbaijan)',
|
||||||
|
az_Cyrl_AZ: 'Azerbaijani (Cyrillic, Azerbaijan)',
|
||||||
|
az_Cyrl: 'Azerbaijani (Cyrillic)',
|
||||||
|
az_Latn_AZ: 'Azerbaijani (Latin, Azerbaijan)',
|
||||||
|
az_Latn: 'Azerbaijani (Latin)',
|
||||||
|
bm: 'Bambara',
|
||||||
|
bm_Latn_ML: 'Bambara (Latin, Mali)',
|
||||||
|
bm_Latn: 'Bambara (Latin)',
|
||||||
|
eu: 'Basque',
|
||||||
|
eu_ES: 'Basque (Spain)',
|
||||||
|
be: 'Belarusian',
|
||||||
|
be_BY: 'Belarusian (Belarus)',
|
||||||
|
bn: 'Bengali',
|
||||||
|
bn_BD: 'Bengali (Bangladesh)',
|
||||||
|
bn_IN: 'Bengali (India)',
|
||||||
|
bs: 'Bosnian',
|
||||||
|
bs_BA: 'Bosnian (Bosnia & Herzegovina)',
|
||||||
|
bs_Cyrl_BA: 'Bosnian (Cyrillic, Bosnia & Herzegovina)',
|
||||||
|
bs_Cyrl: 'Bosnian (Cyrillic)',
|
||||||
|
bs_Latn_BA: 'Bosnian (Latin, Bosnia & Herzegovina)',
|
||||||
|
bs_Latn: 'Bosnian (Latin)',
|
||||||
|
br: 'Breton',
|
||||||
|
br_FR: 'Breton (France)',
|
||||||
|
bg: 'Bulgarian',
|
||||||
|
bg_BG: 'Bulgarian (Bulgaria)',
|
||||||
|
my: 'Burmese',
|
||||||
|
my_MM: 'Burmese (Myanmar (Burma))',
|
||||||
|
ca: 'Catalan',
|
||||||
|
ca_AD: 'Catalan (Andorra)',
|
||||||
|
ca_FR: 'Catalan (France)',
|
||||||
|
ca_IT: 'Catalan (Italy)',
|
||||||
|
ca_ES: 'Catalan (Spain)',
|
||||||
|
zh: 'Chinese',
|
||||||
|
zh_CN: 'Chinese (China)',
|
||||||
|
zh_HK: 'Chinese (Hong Kong SAR China)',
|
||||||
|
zh_MO: 'Chinese (Macau SAR China)',
|
||||||
|
zh_Hans_CN: 'Chinese (Simplified, China)',
|
||||||
|
zh_Hans_HK: 'Chinese (Simplified, Hong Kong SAR China)',
|
||||||
|
zh_Hans_MO: 'Chinese (Simplified, Macau SAR China)',
|
||||||
|
zh_Hans_SG: 'Chinese (Simplified, Singapore)',
|
||||||
|
zh_Hans: 'Chinese (Simplified)',
|
||||||
|
zh_SG: 'Chinese (Singapore)',
|
||||||
|
zh_TW: 'Chinese (Taiwan)',
|
||||||
|
zh_Hant_HK: 'Chinese (Traditional, Hong Kong SAR China)',
|
||||||
|
zh_Hant_MO: 'Chinese (Traditional, Macau SAR China)',
|
||||||
|
zh_Hant_TW: 'Chinese (Traditional, Taiwan)',
|
||||||
|
zh_Hant: 'Chinese (Traditional)',
|
||||||
|
kw: 'Cornish',
|
||||||
|
kw_GB: 'Cornish (United Kingdom)',
|
||||||
|
hr: 'Croatian',
|
||||||
|
hr_BA: 'Croatian (Bosnia & Herzegovina)',
|
||||||
|
hr_HR: 'Croatian (Croatia)',
|
||||||
|
cs: 'Czech',
|
||||||
|
cs_CZ: 'Czech (Czech Republic)',
|
||||||
|
da: 'Danish',
|
||||||
|
da_DK: 'Danish (Denmark)',
|
||||||
|
da_GL: 'Danish (Greenland)',
|
||||||
|
nl: 'Dutch',
|
||||||
|
nl_AW: 'Dutch (Aruba)',
|
||||||
|
nl_BE: 'Dutch (Belgium)',
|
||||||
|
nl_BQ: 'Dutch (Caribbean Netherlands)',
|
||||||
|
nl_CW: 'Dutch (Curaçao)',
|
||||||
|
nl_NL: 'Dutch (Netherlands)',
|
||||||
|
nl_SX: 'Dutch (Sint Maarten)',
|
||||||
|
nl_SR: 'Dutch (Suriname)',
|
||||||
|
dz: 'Dzongkha',
|
||||||
|
dz_BT: 'Dzongkha (Bhutan)',
|
||||||
|
en: 'English',
|
||||||
|
en_AS: 'English (American Samoa)',
|
||||||
|
en_AI: 'English (Anguilla)',
|
||||||
|
en_AG: 'English (Antigua & Barbuda)',
|
||||||
|
en_AU: 'English (Australia)',
|
||||||
|
en_BS: 'English (Bahamas)',
|
||||||
|
en_BB: 'English (Barbados)',
|
||||||
|
en_BE: 'English (Belgium)',
|
||||||
|
en_BZ: 'English (Belize)',
|
||||||
|
en_BM: 'English (Bermuda)',
|
||||||
|
en_BW: 'English (Botswana)',
|
||||||
|
en_IO: 'English (British Indian Ocean Territory)',
|
||||||
|
en_VG: 'English (British Virgin Islands)',
|
||||||
|
en_CM: 'English (Cameroon)',
|
||||||
|
en_CA: 'English (Canada)',
|
||||||
|
en_KY: 'English (Cayman Islands)',
|
||||||
|
en_CX: 'English (Christmas Island)',
|
||||||
|
en_CC: 'English (Cocos (Keeling) Islands)',
|
||||||
|
en_CK: 'English (Cook Islands)',
|
||||||
|
en_DG: 'English (Diego Garcia)',
|
||||||
|
en_DM: 'English (Dominica)',
|
||||||
|
en_ER: 'English (Eritrea)',
|
||||||
|
en_FK: 'English (Falkland Islands)',
|
||||||
|
en_FJ: 'English (Fiji)',
|
||||||
|
en_GM: 'English (Gambia)',
|
||||||
|
en_GH: 'English (Ghana)',
|
||||||
|
en_GI: 'English (Gibraltar)',
|
||||||
|
en_GD: 'English (Grenada)',
|
||||||
|
en_GU: 'English (Guam)',
|
||||||
|
en_GG: 'English (Guernsey)',
|
||||||
|
en_GY: 'English (Guyana)',
|
||||||
|
en_HK: 'English (Hong Kong SAR China)',
|
||||||
|
en_IN: 'English (India)',
|
||||||
|
en_IE: 'English (Ireland)',
|
||||||
|
en_IM: 'English (Isle of Man)',
|
||||||
|
en_JM: 'English (Jamaica)',
|
||||||
|
en_JE: 'English (Jersey)',
|
||||||
|
en_KE: 'English (Kenya)',
|
||||||
|
en_KI: 'English (Kiribati)',
|
||||||
|
en_LS: 'English (Lesotho)',
|
||||||
|
en_LR: 'English (Liberia)',
|
||||||
|
en_MO: 'English (Macau SAR China)',
|
||||||
|
en_MG: 'English (Madagascar)',
|
||||||
|
en_MW: 'English (Malawi)',
|
||||||
|
en_MY: 'English (Malaysia)',
|
||||||
|
en_MT: 'English (Malta)',
|
||||||
|
en_MH: 'English (Marshall Islands)',
|
||||||
|
en_MU: 'English (Mauritius)',
|
||||||
|
en_FM: 'English (Micronesia)',
|
||||||
|
en_MS: 'English (Montserrat)',
|
||||||
|
en_NA: 'English (Namibia)',
|
||||||
|
en_NR: 'English (Nauru)',
|
||||||
|
en_NZ: 'English (New Zealand)',
|
||||||
|
en_NG: 'English (Nigeria)',
|
||||||
|
en_NU: 'English (Niue)',
|
||||||
|
en_NF: 'English (Norfolk Island)',
|
||||||
|
en_MP: 'English (Northern Mariana Islands)',
|
||||||
|
en_PK: 'English (Pakistan)',
|
||||||
|
en_PW: 'English (Palau)',
|
||||||
|
en_PG: 'English (Papua New Guinea)',
|
||||||
|
en_PH: 'English (Philippines)',
|
||||||
|
en_PN: 'English (Pitcairn Islands)',
|
||||||
|
en_PR: 'English (Puerto Rico)',
|
||||||
|
en_RW: 'English (Rwanda)',
|
||||||
|
en_WS: 'English (Samoa)',
|
||||||
|
en_SC: 'English (Seychelles)',
|
||||||
|
en_SL: 'English (Sierra Leone)',
|
||||||
|
en_SG: 'English (Singapore)',
|
||||||
|
en_SX: 'English (Sint Maarten)',
|
||||||
|
en_SB: 'English (Solomon Islands)',
|
||||||
|
en_ZA: 'English (South Africa)',
|
||||||
|
en_SS: 'English (South Sudan)',
|
||||||
|
en_SH: 'English (St. Helena)',
|
||||||
|
en_KN: 'English (St. Kitts & Nevis)',
|
||||||
|
en_LC: 'English (St. Lucia)',
|
||||||
|
en_VC: 'English (St. Vincent & Grenadines)',
|
||||||
|
en_SD: 'English (Sudan)',
|
||||||
|
en_SZ: 'English (Swaziland)',
|
||||||
|
en_TZ: 'English (Tanzania)',
|
||||||
|
en_TK: 'English (Tokelau)',
|
||||||
|
en_TO: 'English (Tonga)',
|
||||||
|
en_TT: 'English (Trinidad & Tobago)',
|
||||||
|
en_TC: 'English (Turks & Caicos Islands)',
|
||||||
|
en_TV: 'English (Tuvalu)',
|
||||||
|
en_UM: 'English (U.S. Outlying Islands)',
|
||||||
|
en_VI: 'English (U.S. Virgin Islands)',
|
||||||
|
en_UG: 'English (Uganda)',
|
||||||
|
en_GB: 'English (United Kingdom)',
|
||||||
|
en_US: 'English (United States)',
|
||||||
|
en_VU: 'English (Vanuatu)',
|
||||||
|
en_ZM: 'English (Zambia)',
|
||||||
|
en_ZW: 'English (Zimbabwe)',
|
||||||
|
eo: 'Esperanto',
|
||||||
|
et: 'Estonian',
|
||||||
|
et_EE: 'Estonian (Estonia)',
|
||||||
|
ee: 'Ewe',
|
||||||
|
ee_GH: 'Ewe (Ghana)',
|
||||||
|
ee_TG: 'Ewe (Togo)',
|
||||||
|
fo: 'Faroese',
|
||||||
|
fo_FO: 'Faroese (Faroe Islands)',
|
||||||
|
fi: 'Finnish',
|
||||||
|
fi_FI: 'Finnish (Finland)',
|
||||||
|
fr: 'French',
|
||||||
|
fr_DZ: 'French (Algeria)',
|
||||||
|
fr_BE: 'French (Belgium)',
|
||||||
|
fr_BJ: 'French (Benin)',
|
||||||
|
fr_BF: 'French (Burkina Faso)',
|
||||||
|
fr_BI: 'French (Burundi)',
|
||||||
|
fr_CM: 'French (Cameroon)',
|
||||||
|
fr_CA: 'French (Canada)',
|
||||||
|
fr_CF: 'French (Central African Republic)',
|
||||||
|
fr_TD: 'French (Chad)',
|
||||||
|
fr_KM: 'French (Comoros)',
|
||||||
|
fr_CG: 'French (Congo - Brazzaville)',
|
||||||
|
fr_CD: 'French (Congo - Kinshasa)',
|
||||||
|
fr_CI: 'French (Côte d’Ivoire)',
|
||||||
|
fr_DJ: 'French (Djibouti)',
|
||||||
|
fr_GQ: 'French (Equatorial Guinea)',
|
||||||
|
fr_FR: 'French (France)',
|
||||||
|
fr_GF: 'French (French Guiana)',
|
||||||
|
fr_PF: 'French (French Polynesia)',
|
||||||
|
fr_GA: 'French (Gabon)',
|
||||||
|
fr_GP: 'French (Guadeloupe)',
|
||||||
|
fr_GN: 'French (Guinea)',
|
||||||
|
fr_HT: 'French (Haiti)',
|
||||||
|
fr_LU: 'French (Luxembourg)',
|
||||||
|
fr_MG: 'French (Madagascar)',
|
||||||
|
fr_ML: 'French (Mali)',
|
||||||
|
fr_MQ: 'French (Martinique)',
|
||||||
|
fr_MR: 'French (Mauritania)',
|
||||||
|
fr_MU: 'French (Mauritius)',
|
||||||
|
fr_YT: 'French (Mayotte)',
|
||||||
|
fr_MC: 'French (Monaco)',
|
||||||
|
fr_MA: 'French (Morocco)',
|
||||||
|
fr_NC: 'French (New Caledonia)',
|
||||||
|
fr_NE: 'French (Niger)',
|
||||||
|
fr_RE: 'French (Réunion)',
|
||||||
|
fr_RW: 'French (Rwanda)',
|
||||||
|
fr_SN: 'French (Senegal)',
|
||||||
|
fr_SC: 'French (Seychelles)',
|
||||||
|
fr_BL: 'French (St. Barthélemy)',
|
||||||
|
fr_MF: 'French (St. Martin)',
|
||||||
|
fr_PM: 'French (St. Pierre & Miquelon)',
|
||||||
|
fr_CH: 'French (Switzerland)',
|
||||||
|
fr_SY: 'French (Syria)',
|
||||||
|
fr_TG: 'French (Togo)',
|
||||||
|
fr_TN: 'French (Tunisia)',
|
||||||
|
fr_VU: 'French (Vanuatu)',
|
||||||
|
fr_WF: 'French (Wallis & Futuna)',
|
||||||
|
ff: 'Fulah',
|
||||||
|
ff_CM: 'Fulah (Cameroon)',
|
||||||
|
ff_GN: 'Fulah (Guinea)',
|
||||||
|
ff_MR: 'Fulah (Mauritania)',
|
||||||
|
ff_SN: 'Fulah (Senegal)',
|
||||||
|
gl: 'Galician',
|
||||||
|
gl_ES: 'Galician (Spain)',
|
||||||
|
lg: 'Ganda',
|
||||||
|
lg_UG: 'Ganda (Uganda)',
|
||||||
|
ka: 'Georgian',
|
||||||
|
ka_GE: 'Georgian (Georgia)',
|
||||||
|
de: 'German',
|
||||||
|
de_AT: 'German (Austria)',
|
||||||
|
de_BE: 'German (Belgium)',
|
||||||
|
de_DE: 'German (Germany)',
|
||||||
|
de_LI: 'German (Liechtenstein)',
|
||||||
|
de_LU: 'German (Luxembourg)',
|
||||||
|
de_CH: 'German (Switzerland)',
|
||||||
|
el: 'Greek',
|
||||||
|
el_CY: 'Greek (Cyprus)',
|
||||||
|
el_GR: 'Greek (Greece)',
|
||||||
|
gu: 'Gujarati',
|
||||||
|
gu_IN: 'Gujarati (India)',
|
||||||
|
ha: 'Hausa',
|
||||||
|
ha_GH: 'Hausa (Ghana)',
|
||||||
|
ha_Latn_GH: 'Hausa (Latin, Ghana)',
|
||||||
|
ha_Latn_NE: 'Hausa (Latin, Niger)',
|
||||||
|
ha_Latn_NG: 'Hausa (Latin, Nigeria)',
|
||||||
|
ha_Latn: 'Hausa (Latin)',
|
||||||
|
ha_NE: 'Hausa (Niger)',
|
||||||
|
ha_NG: 'Hausa (Nigeria)',
|
||||||
|
he: 'Hebrew',
|
||||||
|
he_IL: 'Hebrew (Israel)',
|
||||||
|
hi: 'Hindi',
|
||||||
|
hi_IN: 'Hindi (India)',
|
||||||
|
hu: 'Hungarian',
|
||||||
|
hu_HU: 'Hungarian (Hungary)',
|
||||||
|
is: 'Icelandic',
|
||||||
|
is_IS: 'Icelandic (Iceland)',
|
||||||
|
ig: 'Igbo',
|
||||||
|
ig_NG: 'Igbo (Nigeria)',
|
||||||
|
id: 'Indonesian',
|
||||||
|
id_ID: 'Indonesian (Indonesia)',
|
||||||
|
ga: 'Irish',
|
||||||
|
ga_IE: 'Irish (Ireland)',
|
||||||
|
it: 'Italian',
|
||||||
|
it_IT: 'Italian (Italy)',
|
||||||
|
it_SM: 'Italian (San Marino)',
|
||||||
|
it_CH: 'Italian (Switzerland)',
|
||||||
|
ja: 'Japanese',
|
||||||
|
ja_JP: 'Japanese (Japan)',
|
||||||
|
kl: 'Kalaallisut',
|
||||||
|
kl_GL: 'Kalaallisut (Greenland)',
|
||||||
|
kn: 'Kannada',
|
||||||
|
kn_IN: 'Kannada (India)',
|
||||||
|
ks: 'Kashmiri',
|
||||||
|
ks_Arab_IN: 'Kashmiri (Arabic, India)',
|
||||||
|
ks_Arab: 'Kashmiri (Arabic)',
|
||||||
|
ks_IN: 'Kashmiri (India)',
|
||||||
|
kk: 'Kazakh',
|
||||||
|
kk_Cyrl_KZ: 'Kazakh (Cyrillic, Kazakhstan)',
|
||||||
|
kk_Cyrl: 'Kazakh (Cyrillic)',
|
||||||
|
kk_KZ: 'Kazakh (Kazakhstan)',
|
||||||
|
km: 'Khmer',
|
||||||
|
km_KH: 'Khmer (Cambodia)',
|
||||||
|
ki: 'Kikuyu',
|
||||||
|
ki_KE: 'Kikuyu (Kenya)',
|
||||||
|
rw: 'Kinyarwanda',
|
||||||
|
rw_RW: 'Kinyarwanda (Rwanda)',
|
||||||
|
ko: 'Korean',
|
||||||
|
ko_KP: 'Korean (North Korea)',
|
||||||
|
ko_KR: 'Korean (South Korea)',
|
||||||
|
ky: 'Kyrgyz',
|
||||||
|
ky_Cyrl_KG: 'Kyrgyz (Cyrillic, Kyrgyzstan)',
|
||||||
|
ky_Cyrl: 'Kyrgyz (Cyrillic)',
|
||||||
|
ky_KG: 'Kyrgyz (Kyrgyzstan)',
|
||||||
|
lo: 'Lao',
|
||||||
|
lo_LA: 'Lao (Laos)',
|
||||||
|
lv: 'Latvian',
|
||||||
|
lv_LV: 'Latvian (Latvia)',
|
||||||
|
ln: 'Lingala',
|
||||||
|
ln_AO: 'Lingala (Angola)',
|
||||||
|
ln_CF: 'Lingala (Central African Republic)',
|
||||||
|
ln_CG: 'Lingala (Congo - Brazzaville)',
|
||||||
|
ln_CD: 'Lingala (Congo - Kinshasa)',
|
||||||
|
lt: 'Lithuanian',
|
||||||
|
lt_LT: 'Lithuanian (Lithuania)',
|
||||||
|
lu: 'Luba-Katanga',
|
||||||
|
lu_CD: 'Luba-Katanga (Congo - Kinshasa)',
|
||||||
|
lb: 'Luxembourgish',
|
||||||
|
lb_LU: 'Luxembourgish (Luxembourg)',
|
||||||
|
mk: 'Macedonian',
|
||||||
|
mk_MK: 'Macedonian (Macedonia)',
|
||||||
|
mg: 'Malagasy',
|
||||||
|
mg_MG: 'Malagasy (Madagascar)',
|
||||||
|
ms: 'Malay',
|
||||||
|
ms_BN: 'Malay (Brunei)',
|
||||||
|
ms_Latn_BN: 'Malay (Latin, Brunei)',
|
||||||
|
ms_Latn_MY: 'Malay (Latin, Malaysia)',
|
||||||
|
ms_Latn_SG: 'Malay (Latin, Singapore)',
|
||||||
|
ms_Latn: 'Malay (Latin)',
|
||||||
|
ms_MY: 'Malay (Malaysia)',
|
||||||
|
ms_SG: 'Malay (Singapore)',
|
||||||
|
ml: 'Malayalam',
|
||||||
|
ml_IN: 'Malayalam (India)',
|
||||||
|
mt: 'Maltese',
|
||||||
|
mt_MT: 'Maltese (Malta)',
|
||||||
|
gv: 'Manx',
|
||||||
|
gv_IM: 'Manx (Isle of Man)',
|
||||||
|
mr: 'Marathi',
|
||||||
|
mr_IN: 'Marathi (India)',
|
||||||
|
mn: 'Mongolian',
|
||||||
|
mn_Cyrl_MN: 'Mongolian (Cyrillic, Mongolia)',
|
||||||
|
mn_Cyrl: 'Mongolian (Cyrillic)',
|
||||||
|
mn_MN: 'Mongolian (Mongolia)',
|
||||||
|
ne: 'Nepali',
|
||||||
|
ne_IN: 'Nepali (India)',
|
||||||
|
ne_NP: 'Nepali (Nepal)',
|
||||||
|
nd: 'North Ndebele',
|
||||||
|
nd_ZW: 'North Ndebele (Zimbabwe)',
|
||||||
|
se: 'Northern Sami',
|
||||||
|
se_FI: 'Northern Sami (Finland)',
|
||||||
|
se_NO: 'Northern Sami (Norway)',
|
||||||
|
se_SE: 'Northern Sami (Sweden)',
|
||||||
|
no: 'Norwegian',
|
||||||
|
no_NO: 'Norwegian (Norway)',
|
||||||
|
nb: 'Norwegian Bokmål',
|
||||||
|
nb_NO: 'Norwegian Bokmål (Norway)',
|
||||||
|
nb_SJ: 'Norwegian Bokmål (Svalbard & Jan Mayen)',
|
||||||
|
nn: 'Norwegian Nynorsk',
|
||||||
|
nn_NO: 'Norwegian Nynorsk (Norway)',
|
||||||
|
or: 'Oriya',
|
||||||
|
or_IN: 'Oriya (India)',
|
||||||
|
om: 'Oromo',
|
||||||
|
om_ET: 'Oromo (Ethiopia)',
|
||||||
|
om_KE: 'Oromo (Kenya)',
|
||||||
|
os: 'Ossetic',
|
||||||
|
os_GE: 'Ossetic (Georgia)',
|
||||||
|
os_RU: 'Ossetic (Russia)',
|
||||||
|
ps: 'Pashto',
|
||||||
|
ps_AF: 'Pashto (Afghanistan)',
|
||||||
|
fa: 'Persian',
|
||||||
|
fa_AF: 'Persian (Afghanistan)',
|
||||||
|
fa_IR: 'Persian (Iran)',
|
||||||
|
pl: 'Polish',
|
||||||
|
pl_PL: 'Polish (Poland)',
|
||||||
|
pt: 'Portuguese',
|
||||||
|
pt_AO: 'Portuguese (Angola)',
|
||||||
|
pt_BR: 'Portuguese (Brazil)',
|
||||||
|
pt_CV: 'Portuguese (Cape Verde)',
|
||||||
|
pt_GW: 'Portuguese (Guinea-Bissau)',
|
||||||
|
pt_MO: 'Portuguese (Macau SAR China)',
|
||||||
|
pt_MZ: 'Portuguese (Mozambique)',
|
||||||
|
pt_PT: 'Portuguese (Portugal)',
|
||||||
|
pt_ST: 'Portuguese (São Tomé & Príncipe)',
|
||||||
|
pt_TL: 'Portuguese (Timor-Leste)',
|
||||||
|
pa: 'Punjabi',
|
||||||
|
pa_Arab_PK: 'Punjabi (Arabic, Pakistan)',
|
||||||
|
pa_Arab: 'Punjabi (Arabic)',
|
||||||
|
pa_Guru_IN: 'Punjabi (Gurmukhi, India)',
|
||||||
|
pa_Guru: 'Punjabi (Gurmukhi)',
|
||||||
|
pa_IN: 'Punjabi (India)',
|
||||||
|
pa_PK: 'Punjabi (Pakistan)',
|
||||||
|
qu: 'Quechua',
|
||||||
|
qu_BO: 'Quechua (Bolivia)',
|
||||||
|
qu_EC: 'Quechua (Ecuador)',
|
||||||
|
qu_PE: 'Quechua (Peru)',
|
||||||
|
ro: 'Romanian',
|
||||||
|
ro_MD: 'Romanian (Moldova)',
|
||||||
|
ro_RO: 'Romanian (Romania)',
|
||||||
|
rm: 'Romansh',
|
||||||
|
rm_CH: 'Romansh (Switzerland)',
|
||||||
|
rn: 'Rundi',
|
||||||
|
rn_BI: 'Rundi (Burundi)',
|
||||||
|
ru: 'Russian',
|
||||||
|
ru_BY: 'Russian (Belarus)',
|
||||||
|
ru_KZ: 'Russian (Kazakhstan)',
|
||||||
|
ru_KG: 'Russian (Kyrgyzstan)',
|
||||||
|
ru_MD: 'Russian (Moldova)',
|
||||||
|
ru_RU: 'Russian (Russia)',
|
||||||
|
ru_UA: 'Russian (Ukraine)',
|
||||||
|
sg: 'Sango',
|
||||||
|
sg_CF: 'Sango (Central African Republic)',
|
||||||
|
gd: 'Scottish Gaelic',
|
||||||
|
gd_GB: 'Scottish Gaelic (United Kingdom)',
|
||||||
|
sr: 'Serbian',
|
||||||
|
sr_BA: 'Serbian (Bosnia & Herzegovina)',
|
||||||
|
sr_Cyrl_BA: 'Serbian (Cyrillic, Bosnia & Herzegovina)',
|
||||||
|
sr_Cyrl_XK: 'Serbian (Cyrillic, Kosovo)',
|
||||||
|
sr_Cyrl_ME: 'Serbian (Cyrillic, Montenegro)',
|
||||||
|
sr_Cyrl_RS: 'Serbian (Cyrillic, Serbia)',
|
||||||
|
sr_Cyrl: 'Serbian (Cyrillic)',
|
||||||
|
sr_XK: 'Serbian (Kosovo)',
|
||||||
|
sr_Latn_BA: 'Serbian (Latin, Bosnia & Herzegovina)',
|
||||||
|
sr_Latn_XK: 'Serbian (Latin, Kosovo)',
|
||||||
|
sr_Latn_ME: 'Serbian (Latin, Montenegro)',
|
||||||
|
sr_Latn_RS: 'Serbian (Latin, Serbia)',
|
||||||
|
sr_Latn: 'Serbian (Latin)',
|
||||||
|
sr_ME: 'Serbian (Montenegro)',
|
||||||
|
sr_RS: 'Serbian (Serbia)',
|
||||||
|
sh: 'Serbo-Croatian',
|
||||||
|
sh_BA: 'Serbo-Croatian (Bosnia & Herzegovina)',
|
||||||
|
sn: 'Shona',
|
||||||
|
sn_ZW: 'Shona (Zimbabwe)',
|
||||||
|
ii: 'Sichuan Yi',
|
||||||
|
ii_CN: 'Sichuan Yi (China)',
|
||||||
|
si: 'Sinhala',
|
||||||
|
si_LK: 'Sinhala (Sri Lanka)',
|
||||||
|
sk: 'Slovak',
|
||||||
|
sk_SK: 'Slovak (Slovakia)',
|
||||||
|
sl: 'Slovenian',
|
||||||
|
sl_SI: 'Slovenian (Slovenia)',
|
||||||
|
so: 'Somali',
|
||||||
|
so_DJ: 'Somali (Djibouti)',
|
||||||
|
so_ET: 'Somali (Ethiopia)',
|
||||||
|
so_KE: 'Somali (Kenya)',
|
||||||
|
so_SO: 'Somali (Somalia)',
|
||||||
|
es: 'Spanish',
|
||||||
|
es_AR: 'Spanish (Argentina)',
|
||||||
|
es_BO: 'Spanish (Bolivia)',
|
||||||
|
es_IC: 'Spanish (Canary Islands)',
|
||||||
|
es_EA: 'Spanish (Ceuta & Melilla)',
|
||||||
|
es_CL: 'Spanish (Chile)',
|
||||||
|
es_CO: 'Spanish (Colombia)',
|
||||||
|
es_CR: 'Spanish (Costa Rica)',
|
||||||
|
es_CU: 'Spanish (Cuba)',
|
||||||
|
es_DO: 'Spanish (Dominican Republic)',
|
||||||
|
es_EC: 'Spanish (Ecuador)',
|
||||||
|
es_SV: 'Spanish (El Salvador)',
|
||||||
|
es_GQ: 'Spanish (Equatorial Guinea)',
|
||||||
|
es_GT: 'Spanish (Guatemala)',
|
||||||
|
es_HN: 'Spanish (Honduras)',
|
||||||
|
es_MX: 'Spanish (Mexico)',
|
||||||
|
es_NI: 'Spanish (Nicaragua)',
|
||||||
|
es_PA: 'Spanish (Panama)',
|
||||||
|
es_PY: 'Spanish (Paraguay)',
|
||||||
|
es_PE: 'Spanish (Peru)',
|
||||||
|
es_PH: 'Spanish (Philippines)',
|
||||||
|
es_PR: 'Spanish (Puerto Rico)',
|
||||||
|
es_ES: 'Spanish (Spain)',
|
||||||
|
es_US: 'Spanish (United States)',
|
||||||
|
es_UY: 'Spanish (Uruguay)',
|
||||||
|
es_VE: 'Spanish (Venezuela)',
|
||||||
|
sw: 'Swahili',
|
||||||
|
sw_KE: 'Swahili (Kenya)',
|
||||||
|
sw_TZ: 'Swahili (Tanzania)',
|
||||||
|
sw_UG: 'Swahili (Uganda)',
|
||||||
|
sv: 'Swedish',
|
||||||
|
sv_AX: 'Swedish (Åland Islands)',
|
||||||
|
sv_FI: 'Swedish (Finland)',
|
||||||
|
sv_SE: 'Swedish (Sweden)',
|
||||||
|
tl: 'Tagalog',
|
||||||
|
tl_PH: 'Tagalog (Philippines)',
|
||||||
|
ta: 'Tamil',
|
||||||
|
ta_IN: 'Tamil (India)',
|
||||||
|
ta_MY: 'Tamil (Malaysia)',
|
||||||
|
ta_SG: 'Tamil (Singapore)',
|
||||||
|
ta_LK: 'Tamil (Sri Lanka)',
|
||||||
|
te: 'Telugu',
|
||||||
|
te_IN: 'Telugu (India)',
|
||||||
|
th: 'Thai',
|
||||||
|
th_TH: 'Thai (Thailand)',
|
||||||
|
bo: 'Tibetan',
|
||||||
|
bo_CN: 'Tibetan (China)',
|
||||||
|
bo_IN: 'Tibetan (India)',
|
||||||
|
ti: 'Tigrinya',
|
||||||
|
ti_ER: 'Tigrinya (Eritrea)',
|
||||||
|
ti_ET: 'Tigrinya (Ethiopia)',
|
||||||
|
to: 'Tongan',
|
||||||
|
to_TO: 'Tongan (Tonga)',
|
||||||
|
tr: 'Turkish',
|
||||||
|
tr_CY: 'Turkish (Cyprus)',
|
||||||
|
tr_TR: 'Turkish (Turkey)',
|
||||||
|
uk: 'Ukrainian',
|
||||||
|
uk_UA: 'Ukrainian (Ukraine)',
|
||||||
|
ur: 'Urdu',
|
||||||
|
ur_IN: 'Urdu (India)',
|
||||||
|
ur_PK: 'Urdu (Pakistan)',
|
||||||
|
ug: 'Uyghur',
|
||||||
|
ug_Arab_CN: 'Uyghur (Arabic, China)',
|
||||||
|
ug_Arab: 'Uyghur (Arabic)',
|
||||||
|
ug_CN: 'Uyghur (China)',
|
||||||
|
uz: 'Uzbek',
|
||||||
|
uz_AF: 'Uzbek (Afghanistan)',
|
||||||
|
uz_Arab_AF: 'Uzbek (Arabic, Afghanistan)',
|
||||||
|
uz_Arab: 'Uzbek (Arabic)',
|
||||||
|
uz_Cyrl_UZ: 'Uzbek (Cyrillic, Uzbekistan)',
|
||||||
|
uz_Cyrl: 'Uzbek (Cyrillic)',
|
||||||
|
uz_Latn_UZ: 'Uzbek (Latin, Uzbekistan)',
|
||||||
|
uz_Latn: 'Uzbek (Latin)',
|
||||||
|
uz_UZ: 'Uzbek (Uzbekistan)',
|
||||||
|
vi: 'Vietnamese',
|
||||||
|
vi_VN: 'Vietnamese (Vietnam)',
|
||||||
|
cy: 'Welsh',
|
||||||
|
cy_GB: 'Welsh (United Kingdom)',
|
||||||
|
fy: 'Western Frisian',
|
||||||
|
fy_NL: 'Western Frisian (Netherlands)',
|
||||||
|
yi: 'Yiddish',
|
||||||
|
yo: 'Yoruba',
|
||||||
|
yo_BJ: 'Yoruba (Benin)',
|
||||||
|
yo_NG: 'Yoruba (Nigeria)',
|
||||||
|
zu: 'Zulu',
|
||||||
|
zu_ZA: 'Zulu (South Africa)',
|
||||||
|
};
|
||||||
|
export default locales;
|
|
@ -1,5 +1,4 @@
|
||||||
json.id article.id
|
json.id article.id
|
||||||
json.category_id article.category_id
|
|
||||||
json.title article.title
|
json.title article.title
|
||||||
json.content article.content
|
json.content article.content
|
||||||
json.description article.description
|
json.description article.description
|
||||||
|
@ -7,6 +6,11 @@ json.status article.status
|
||||||
json.account_id article.account_id
|
json.account_id article.account_id
|
||||||
json.updated_at article.updated_at.to_i
|
json.updated_at article.updated_at.to_i
|
||||||
|
|
||||||
|
json.category do
|
||||||
|
json.id article.category_id
|
||||||
|
json.name article.category.name
|
||||||
|
end
|
||||||
|
|
||||||
if article.portal.present?
|
if article.portal.present?
|
||||||
json.portal do
|
json.portal do
|
||||||
json.partial! 'api/v1/accounts/portals/portal.json.jbuilder', portal: article.portal
|
json.partial! 'api/v1/accounts/portals/portal.json.jbuilder', portal: article.portal
|
||||||
|
|
Loading…
Reference in a new issue