Compare commits

...

8 commits

Author SHA1 Message Date
Muhsin Keloth
f64b5be46f Add set default locale 2022-09-01 12:44:13 +05:30
Muhsin Keloth
72e841e36b Minor fixes 2022-09-01 11:29:39 +05:30
Muhsin Keloth
e7a128e04a Complete add locale feature 2022-09-01 11:28:40 +05:30
Muhsin Keloth
a7a4352cad Merge branch 'feat/add-locale' of https://github.com/chatwoot/chatwoot into feat/add-locale 2022-09-01 11:17:41 +05:30
Muhsin Keloth
91bfc60e8f
Merge branch 'develop' into feat/add-locale 2022-09-01 11:17:18 +05:30
Muhsin Keloth
9728aa357c Hide add locale button in article page 2022-09-01 11:12:28 +05:30
Muhsin Keloth
9efc37e85a Shoe locales in dropdown 2022-08-31 20:16:59 +05:30
Muhsin Keloth
6d817b2f02 Add locale modal 2022-08-31 18:35:42 +05:30
10 changed files with 258 additions and 213 deletions

View file

@ -5,9 +5,6 @@
{{ $t(`SIDEBAR.${menuItem.label}`) }} {{ $t(`SIDEBAR.${menuItem.label}`) }}
</span> </span>
<div v-if="isHelpCenterSidebar" class="submenu-icons"> <div v-if="isHelpCenterSidebar" class="submenu-icons">
<div class="submenu-icon">
<fluent-icon icon="search" size="16" />
</div>
<div class="submenu-icon" @click="onClickOpen"> <div class="submenu-icon" @click="onClickOpen">
<fluent-icon icon="add" size="16" /> <fluent-icon icon="add" size="16" />
</div> </div>

View file

@ -199,6 +199,36 @@
"ERROR_MESSAGE_FOR_BASIC": "Couldn't create the portal. Try again.", "ERROR_MESSAGE_FOR_BASIC": "Couldn't create the portal. Try again.",
"ERROR_MESSAGE_FOR_UPDATE": "Couldn't update the portal. Try again." "ERROR_MESSAGE_FOR_UPDATE": "Couldn't update the portal. Try again."
} }
},
"ADD_LOCALE": {
"TITLE": "Add a new locale",
"SUB_TITLE": "This adds a new locale to your available translation list.",
"PORTAL": "Portal",
"LOCALE": {
"LABEL": "Locale",
"PLACEHOLDER": "Choose a locale",
"ERROR": "Locale is required"
},
"BUTTONS": {
"CREATE": "Create locale",
"CANCEL": "Cancel"
},
"API": {
"SUCCESS_MESSAGE": "Locale added successfully",
"ERROR_MESSAGE": "Unable to add locale. Try again."
}
},
"CHANGE_DEFAULT_LOCALE": {
"API": {
"SUCCESS_MESSAGE": "Default locale updated successfully",
"ERROR_MESSAGE": "Unable to update default locale. Try again."
}
},
"DELETE_LOCALE": {
"API": {
"SUCCESS_MESSAGE": "Locale removed from portal successfully",
"ERROR_MESSAGE": "Unable to remove locale from portal. Try again."
}
} }
}, },
"TABLE": { "TABLE": {

View file

@ -0,0 +1,136 @@
<template>
<modal :show.sync="show" :on-close="onClose">
<woot-modal-header
:header-title="$t('HELP_CENTER.PORTAL.ADD_LOCALE.TITLE')"
:header-content="$t('HELP_CENTER.PORTAL.ADD_LOCALE.SUB_TITLE')"
/>
<form class="row" @submit.prevent="onCreate">
<div class="medium-12 columns">
<label :class="{ error: $v.selectedLocale.$error }">
{{ $t('HELP_CENTER.PORTAL.ADD_LOCALE.LOCALE.LABEL') }}
<select v-model="selectedLocale">
<option
v-for="item in allLocales"
:key="item.name"
:value="item.id"
>
{{ item.name }}-{{ item.code }}
</option>
</select>
<span v-if="$v.selectedLocale.$error" class="message">
{{ $t('HELP_CENTER.PORTAL.ADD_LOCALE.LOCALE.ERROR') }}
</span>
</label>
<div class="medium-12 columns">
<div class="modal-footer justify-content-end w-full">
<woot-button class="button clear" @click.prevent="onClose">
{{ $t('HELP_CENTER.PORTAL.ADD_LOCALE.BUTTONS.CANCEL') }}
</woot-button>
<woot-button>
{{ $t('HELP_CENTER.PORTAL.ADD_LOCALE.BUTTONS.CREATE') }}
</woot-button>
</div>
</div>
</div>
</form>
</modal>
</template>
<script>
import Modal from 'dashboard/components/Modal';
import alertMixin from 'shared/mixins/alertMixin';
import { required } from 'vuelidate/lib/validators';
import allLocales from 'shared/constants/locales.js';
export default {
components: {
Modal,
},
mixins: [alertMixin],
props: {
show: {
type: Boolean,
default: true,
},
portal: {
type: Object,
default: () => ({}),
},
},
data() {
return {
selectedLocale: '',
isUpdating: false,
};
},
computed: {
allLocales() {
const addedLocales = this.portal.config.allowed_locales.map(
locale => locale.code
);
return Object.keys(allLocales)
.map(key => {
return {
id: key,
name: allLocales[key],
code: key,
};
})
.filter(locale => {
return !addedLocales.includes(locale.code);
});
},
},
validations: {
selectedLocale: {
required,
},
},
methods: {
async onCreate() {
this.$v.$touch();
if (this.$v.$invalid) {
return;
}
const { allowed_locales: allowedLocales } = this.portal.config;
const addedLocales = Object.keys(allowedLocales).map(key => {
return allowedLocales[key].code;
});
addedLocales.push(this.selectedLocale);
this.isUpdating = true;
try {
await this.$store.dispatch('portals/update', {
portalSlug: this.portal.slug,
config: { allowed_locales: addedLocales },
});
this.alertMessage = this.$t(
'HELP_CENTER.PORTAL.ADD_LOCALE.API.SUCCESS_MESSAGE'
);
this.onClose();
} catch (error) {
this.alertMessage =
error?.message ||
this.$t('HELP_CENTER.PORTAL.ADD_LOCALE.API.ERROR_MESSAGE');
} finally {
this.showAlert(this.alertMessage);
this.isUpdating = false;
}
},
onClose() {
this.$emit('cancel');
},
},
};
</script>
<style scoped lang="scss">
.input-container::v-deep {
margin: 0 0 var(--space-normal);
input {
margin-bottom: 0;
}
.message {
margin-top: 0;
}
}
</style>

View file

@ -1,185 +0,0 @@
<template>
<modal :show.sync="show" :on-close="onClose">
<woot-modal-header
:header-title="$t('HELP_CENTER.PORTAL.ADD.TITLE')"
:header-content="$t('HELP_CENTER.PORTAL.ADD.SUB_TITLE')"
/>
<form class="row" @submit.prevent="onCreate">
<div class="medium-12 columns">
<woot-input
v-model="name"
:class="{ error: $v.name.$error }"
class="medium-12 columns"
:error="$v.name.$error ? $t('HELP_CENTER.PORTAL.ADD.NAME.ERROR') : ''"
:label="$t('HELP_CENTER.PORTAL.ADD.NAME.LABEL')"
:placeholder="$t('HELP_CENTER.PORTAL.ADD.NAME.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.PORTAL.ADD.NAME.HELP_TEXT')"
@blur="$v.name.$touch"
@input="onNameChange"
/>
<woot-input
v-model="slug"
:class="{ error: $v.slug.$error }"
class="medium-12 columns"
:error="$v.slug.$error ? $t('HELP_CENTER.PORTAL.ADD.SLUG.ERROR') : ''"
:label="$t('HELP_CENTER.PORTAL.ADD.SLUG.LABEL')"
:placeholder="$t('HELP_CENTER.PORTAL.ADD.SLUG.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.PORTAL.ADD.SLUG.HELP_TEXT')"
@blur="$v.slug.$touch"
/>
<woot-input
v-model="pageTitle"
class="medium-12 columns"
:label="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.LABEL')"
:placeholder="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.PORTAL.ADD.PAGE_TITLE.HELP_TEXT')"
/>
<woot-input
v-model="headerText"
class="medium-12 columns"
:label="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.LABEL')"
:placeholder="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.PORTAL.ADD.HEADER_TEXT.HELP_TEXT')"
/>
<woot-input
v-model="domain"
class="medium-12 columns"
:label="$t('HELP_CENTER.PORTAL.ADD.DOMAIN.LABEL')"
:placeholder="$t('HELP_CENTER.PORTAL.ADD.DOMAIN.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.PORTAL.ADD.DOMAIN.HELP_TEXT')"
/>
<woot-input
v-model="homePageLink"
class="medium-12 columns"
:label="$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.LABEL')"
:placeholder="$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.PLACEHOLDER')"
:help-text="$t('HELP_CENTER.PORTAL.ADD.HOME_PAGE_LINK.HELP_TEXT')"
/>
<div class="medium-12 columns">
<div class="modal-footer justify-content-end w-full">
<woot-button
class="button clear"
:is-loading="uiFlags.isCreating"
@click.prevent="onClose"
>
{{ $t('HELP_CENTER.PORTAL.ADD.BUTTONS.CANCEL') }}
</woot-button>
<woot-button>
{{ $t('HELP_CENTER.PORTAL.ADD.BUTTONS.CREATE') }}
</woot-button>
</div>
</div>
</div>
</form>
</modal>
</template>
<script>
import { mapGetters } from 'vuex';
import Modal from 'dashboard/components/Modal';
import alertMixin from 'shared/mixins/alertMixin';
import { required } from 'vuelidate/lib/validators';
import { convertToPortalSlug } from 'dashboard/helper/commons.js';
export default {
components: {
Modal,
},
mixins: [alertMixin],
props: {
show: {
type: Boolean,
default: true,
},
portalName: {
type: String,
default: '',
},
locale: {
type: String,
default: '',
},
},
data() {
return {
name: '',
slug: '',
domain: '',
homePageLink: '',
pageTitle: '',
headerText: '',
alertMessage: '',
};
},
computed: {
...mapGetters({
uiFlags: 'portals/uiFlagsIn',
}),
},
validations: {
name: {
required,
},
slug: {
required,
},
},
methods: {
onNameChange() {
this.slug = convertToPortalSlug(this.name);
},
async onCreate() {
this.$v.$touch();
if (this.$v.$invalid) {
return;
}
try {
await this.$store.dispatch('portals/create', {
portal: {
name: this.name,
slug: this.slug,
custom_domain: this.domain,
// TODO: add support for choosing color
color: '#1f93ff',
homepage_link: this.homePageLink,
page_title: this.pageTitle,
header_text: this.headerText,
config: {
// TODO: add support for choosing locale
allowed_locales: ['en'],
},
},
});
this.alertMessage = this.$t(
'HELP_CENTER.PORTAL.ADD.API.SUCCESS_MESSAGE'
);
this.$emit('cancel');
} catch (error) {
this.alertMessage =
error?.message || this.$t('HELP_CENTER.PORTAL.ADD.API.ERROR_MESSAGE');
} finally {
this.showAlert(this.alertMessage);
}
},
onClose() {
this.$emit('cancel');
},
},
};
</script>
<style scoped lang="scss">
.input-container::v-deep {
margin: 0 0 var(--space-normal);
input {
margin-bottom: 0;
}
.message {
margin-top: 0;
}
}
</style>

View file

@ -27,6 +27,7 @@
{{ $t('HELP_CENTER.EDIT_HEADER.PREVIEW') }} {{ $t('HELP_CENTER.EDIT_HEADER.PREVIEW') }}
</woot-button> </woot-button>
<woot-button <woot-button
v-if="shouldShowAddLocaleButton"
class-names="article--buttons" class-names="article--buttons"
icon="add" icon="add"
color-scheme="secondary" color-scheme="secondary"
@ -87,6 +88,10 @@ export default {
type: Boolean, type: Boolean,
default: false, default: false,
}, },
shouldShowAddLocaleButton: {
type: Boolean,
default: false,
},
}, },
data() { data() {
return { return {

View file

@ -142,7 +142,7 @@
<locale-item-table <locale-item-table
:locales="locales" :locales="locales"
:selected-locale-code="portal.meta.default_locale" :selected-locale-code="portal.meta.default_locale"
@swap="swapLocale" @change-default-locale="changeDefaultLocale"
@delete="deleteLocale" @delete="deleteLocale"
/> />
</div> </div>
@ -152,6 +152,7 @@
</template> </template>
<script> <script>
import alertMixin from 'shared/mixins/alertMixin';
import thumbnail from 'dashboard/components/widgets/Thumbnail'; import thumbnail from 'dashboard/components/widgets/Thumbnail';
import Label from 'dashboard/components/ui/Label'; import Label from 'dashboard/components/ui/Label';
import LocaleItemTable from './PortalListItemTable'; import LocaleItemTable from './PortalListItemTable';
@ -161,6 +162,7 @@ export default {
Label, Label,
LocaleItemTable, LocaleItemTable,
}, },
mixins: [alertMixin],
props: { props: {
portal: { portal: {
type: Object, type: Object,
@ -188,7 +190,7 @@ export default {
}, },
methods: { methods: {
addLocale() { addLocale() {
this.$emit('add'); this.$emit('add-locale', this.portal.id);
}, },
openSite() { openSite() {
this.$emit('open-site'); this.$emit('open-site');
@ -196,11 +198,60 @@ export default {
openSettings() { openSettings() {
this.navigateToPortalEdit(); this.navigateToPortalEdit();
}, },
swapLocale() { changeDefaultLocale(localeCode) {
this.$emit('swap'); const { allowed_locales: allowedLocales } = this.portal.config;
const locales = Object.keys(allowedLocales).map(key => {
return allowedLocales[key].code;
});
this.updatePortalLocales({
allowedLocales: locales,
defaultLocale: localeCode,
successMessage: this.$t(
'HELP_CENTER.PORTAL.CHANGE_DEFAULT_LOCALE.API.SUCCESS_MESSAGE'
),
errorMessage: this.$t(
'HELP_CENTER.PORTAL.CHANGE_DEFAULT_LOCALE.API.ERROR_MESSAGE'
),
});
}, },
deleteLocale() { deleteLocale(localeCode) {
this.$emit('delete'); const { allowed_locales: allowedLocales } = this.portal.config;
const addedLocales = Object.keys(allowedLocales).map(key => {
return allowedLocales[key].code;
});
const newLocales = addedLocales.filter(code => code !== localeCode);
const defaultLocale = this.portal.meta.default_locale;
this.updatePortalLocales({
allowedLocales: newLocales,
defaultLocale,
successMessage: this.$t(
'HELP_CENTER.PORTAL.DELETE_LOCALE.API.SUCCESS_MESSAGE'
),
errorMessage: this.$t(
'HELP_CENTER.PORTAL.DELETE_LOCALE.API.ERROR_MESSAGE'
),
});
},
async updatePortalLocales({
allowedLocales,
defaultLocale,
successMessage,
errorMessage,
}) {
try {
await this.$store.dispatch('portals/update', {
portalSlug: this.portal.slug,
config: {
default_locale: defaultLocale,
allowed_locales: allowedLocales,
},
});
this.alertMessage = successMessage;
} catch (error) {
this.alertMessage = error?.message || errorMessage;
} finally {
this.showAlert(this.alertMessage);
}
}, },
navigateToPortalEdit() { navigateToPortalEdit() {
this.$router.push({ this.$router.push({

View file

@ -73,7 +73,7 @@
icon="arrow-swap" icon="arrow-swap"
color-scheme="primary" color-scheme="primary"
:disabled="locale.code === selectedLocaleCode" :disabled="locale.code === selectedLocaleCode"
@click="swapLocale" @click="changeDefaultLocale(locale.code)"
/> />
<woot-button <woot-button
v-tooltip.top-end=" v-tooltip.top-end="
@ -85,7 +85,8 @@
variant="smooth" variant="smooth"
icon="delete" icon="delete"
color-scheme="secondary" color-scheme="secondary"
@click="deleteLocale" :disabled="locale.code === selectedLocaleCode"
@click="deleteLocale(locale.code)"
/> />
</td> </td>
</tr> </tr>
@ -113,11 +114,11 @@ export default {
}, },
methods: { methods: {
swapLocale() { changeDefaultLocale(localeCode) {
this.$emit('swap'); this.$emit('change-default-locale', localeCode);
}, },
deleteLocale() { deleteLocale(localeCode) {
this.$emit('delete'); this.$emit('delete', localeCode);
}, },
}, },
}; };

View file

@ -6,7 +6,6 @@
:sub-title="subTitle" :sub-title="subTitle"
@open-popover="openPortalPopover" @open-popover="openPortalPopover"
/> />
<sidebar-search @input="onSearch" />
<transition-group name="menu-list" tag="ul" class="menu vertical"> <transition-group name="menu-list" tag="ul" class="menu vertical">
<secondary-nav-item <secondary-nav-item
v-for="menuItem in accessibleMenuItems" v-for="menuItem in accessibleMenuItems"
@ -28,13 +27,11 @@
<script> <script>
import SecondaryNavItem from 'dashboard/components/layout/sidebarComponents/SecondaryNavItem'; import SecondaryNavItem from 'dashboard/components/layout/sidebarComponents/SecondaryNavItem';
import SidebarSearch from './SidebarSearch';
import SidebarHeader from './SidebarHeader'; import SidebarHeader from './SidebarHeader';
export default { export default {
components: { components: {
SecondaryNavItem, SecondaryNavItem,
SidebarSearch,
SidebarHeader, SidebarHeader,
}, },
props: { props: {

View file

@ -15,6 +15,7 @@
:key="portal.id" :key="portal.id"
:portal="portal" :portal="portal"
:status="portalStatus" :status="portalStatus"
@add-locale="addLocale"
/> />
<div v-if="isFetching" class="portals--loader"> <div v-if="isFetching" class="portals--loader">
<spinner /> <spinner />
@ -25,8 +26,15 @@
:title="$t('HELP_CENTER.PORTAL.NO_PORTALS_MESSAGE')" :title="$t('HELP_CENTER.PORTAL.NO_PORTALS_MESSAGE')"
/> />
</div> </div>
<woot-modal :show.sync="isAddModalOpen" :on-close="closeModal"> <woot-modal
<add-portal :show="isAddModalOpen" @cancel="closeModal" /> :show.sync="isAddLocaleModalOpen"
:on-close="closeAddLocaleModal"
>
<add-locale
:show="isAddLocaleModalOpen"
:portal="selectedPortal"
@cancel="closeAddLocaleModal"
/>
</woot-modal> </woot-modal>
</div> </div>
</template> </template>
@ -36,17 +44,18 @@ import { mapGetters } from 'vuex';
import PortalListItem from '../../components/PortalListItem'; import PortalListItem from '../../components/PortalListItem';
import Spinner from 'shared/components/Spinner.vue'; import Spinner from 'shared/components/Spinner.vue';
import EmptyState from 'dashboard/components/widgets/EmptyState'; import EmptyState from 'dashboard/components/widgets/EmptyState';
import AddPortal from '../../components/AddPortal'; import AddLocale from '../../components/AddLocale';
export default { export default {
components: { components: {
PortalListItem, PortalListItem,
EmptyState, EmptyState,
Spinner, Spinner,
AddPortal, AddLocale,
}, },
data() { data() {
return { return {
isAddModalOpen: false, isAddLocaleModalOpen: false,
selectedPortal: {},
}; };
}, },
computed: { computed: {
@ -66,8 +75,13 @@ export default {
addPortal() { addPortal() {
this.$router.push({ name: 'new_portal_information' }); this.$router.push({ name: 'new_portal_information' });
}, },
closeModal() { closeAddLocaleModal() {
this.isAddModalOpen = false; this.isAddLocaleModalOpen = false;
this.selectedPortal = {};
},
addLocale(portalId) {
this.isAddLocaleModalOpen = true;
this.selectedPortal = this.portals.find(portal => portal.id === portalId);
}, },
}, },
}; };

View file

@ -36,8 +36,7 @@ export const actions = {
} }
}, },
update: async ({ commit }, { portalObj }) => { update: async ({ commit }, { portalSlug, ...params }) => {
const portalSlug = portalObj.slug;
commit(types.SET_HELP_PORTAL_UI_FLAG, { commit(types.SET_HELP_PORTAL_UI_FLAG, {
uiFlags: { isUpdating: true }, uiFlags: { isUpdating: true },
portalSlug, portalSlug,
@ -45,7 +44,7 @@ export const actions = {
try { try {
const { data } = await portalAPIs.updatePortal({ const { data } = await portalAPIs.updatePortal({
portalSlug, portalSlug,
portalObj, params,
}); });
commit(types.UPDATE_PORTAL_ENTRY, data); commit(types.UPDATE_PORTAL_ENTRY, data);
} catch (error) { } catch (error) {