mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 12:08:01 +00:00
feat: Replace rtlMixin to a composable (#9924)
This PR will replace the usage of `rtlMixin` to the `useUISettings` composable, and moved the method to component itself.
This commit is contained in:
@@ -10,7 +10,6 @@ import PaymentPendingBanner from './components/app/PaymentPendingBanner.vue';
|
||||
import PendingEmailVerificationBanner from './components/app/PendingEmailVerificationBanner.vue';
|
||||
import vueActionCable from './helper/actionCable';
|
||||
import WootSnackbarBox from './components/SnackbarContainer.vue';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import { setColorTheme } from './helper/themeHelper';
|
||||
import { isOnOnboardingView } from 'v3/helpers/RouteHelper';
|
||||
import {
|
||||
@@ -32,9 +31,6 @@ export default {
|
||||
UpgradeBanner,
|
||||
PendingEmailVerificationBanner,
|
||||
},
|
||||
|
||||
mixins: [rtlMixin],
|
||||
|
||||
data() {
|
||||
return {
|
||||
showAddAccountModal: false,
|
||||
@@ -46,6 +42,7 @@ export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
getAccount: 'accounts/getAccount',
|
||||
isRTL: 'accounts/isRTL',
|
||||
currentUser: 'getCurrentUser',
|
||||
authUIFlags: 'getAuthUIFlags',
|
||||
accountUIFlags: 'accounts/getUIFlags',
|
||||
@@ -102,7 +99,6 @@ export default {
|
||||
this.getAccount(this.currentAccountId);
|
||||
const { pubsub_token: pubsubToken } = this.currentUser || {};
|
||||
this.setLocale(locale);
|
||||
this.updateRTLDirectionView(locale);
|
||||
this.latestChatwootVersion = latestChatwootVersion;
|
||||
vueActionCable.init(pubsubToken);
|
||||
this.reconnectService = new ReconnectService(this.$store, router);
|
||||
@@ -124,8 +120,8 @@ export default {
|
||||
v-if="!authUIFlags.isFetching && !accountUIFlags.isFetchingItem"
|
||||
id="app"
|
||||
class="flex-grow-0 w-full h-full min-h-0 app-wrapper"
|
||||
:class="{ 'app-rtl--wrapper': isRTLView }"
|
||||
:dir="isRTLView ? 'rtl' : 'ltr'"
|
||||
:class="{ 'app-rtl--wrapper': isRTL }"
|
||||
:dir="isRTL ? 'rtl' : 'ltr'"
|
||||
>
|
||||
<UpdateBanner :latest-chatwoot-version="latestChatwootVersion" />
|
||||
<template v-if="currentAccountId">
|
||||
|
||||
@@ -31,7 +31,6 @@ import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
||||
import { trimContent, debounce } from '@chatwoot/utils';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
||||
import {
|
||||
appendSignature,
|
||||
@@ -65,7 +64,6 @@ export default {
|
||||
mixins: [
|
||||
inboxMixin,
|
||||
messageFormatterMixin,
|
||||
rtlMixin,
|
||||
fileUploadMixin,
|
||||
keyboardEventListenerMixins,
|
||||
],
|
||||
@@ -121,6 +119,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
isRTL: 'accounts/isRTL',
|
||||
currentChat: 'getSelectedChat',
|
||||
messageSignature: 'getMessageSignature',
|
||||
currentUser: 'getCurrentUser',
|
||||
@@ -307,7 +306,7 @@ export default {
|
||||
if (this.isOnExpandedLayout || this.popoutReplyBox) {
|
||||
return 'emoji-dialog--expanded';
|
||||
}
|
||||
if (this.isRTLView) {
|
||||
if (this.isRTL) {
|
||||
return 'emoji-dialog--rtl';
|
||||
}
|
||||
return '';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { VeTable } from 'vue-easytable';
|
||||
import { getCountryFlag } from 'dashboard/helper/flag';
|
||||
|
||||
@@ -6,7 +7,6 @@ import Spinner from 'shared/components/Spinner.vue';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
|
||||
import { dynamicTime } from 'shared/helpers/timeHelper';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
||||
|
||||
export default {
|
||||
@@ -15,7 +15,6 @@ export default {
|
||||
Spinner,
|
||||
VeTable,
|
||||
},
|
||||
mixins: [rtlMixin],
|
||||
props: {
|
||||
contacts: {
|
||||
type: Array,
|
||||
@@ -52,6 +51,9 @@ export default {
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
isRTL: 'accounts/isRTL',
|
||||
}),
|
||||
tableData() {
|
||||
if (this.isLoading) {
|
||||
return [];
|
||||
@@ -85,7 +87,7 @@ export default {
|
||||
key: 'name',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.NAME'),
|
||||
fixed: 'left',
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
sortBy: this.sortConfig.name || '',
|
||||
width: 300,
|
||||
renderBodyCell: ({ row }) => (
|
||||
@@ -121,7 +123,7 @@ export default {
|
||||
field: 'email',
|
||||
key: 'email',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.EMAIL_ADDRESS'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
sortBy: this.sortConfig.email || '',
|
||||
width: 240,
|
||||
renderBodyCell: ({ row }) => {
|
||||
@@ -145,27 +147,27 @@ export default {
|
||||
key: 'phone_number',
|
||||
sortBy: this.sortConfig.phone_number || '',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.PHONE_NUMBER'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
},
|
||||
{
|
||||
field: 'company',
|
||||
key: 'company',
|
||||
sortBy: this.sortConfig.company_name || '',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.COMPANY'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
},
|
||||
{
|
||||
field: 'city',
|
||||
key: 'city',
|
||||
sortBy: this.sortConfig.city || '',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.CITY'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
},
|
||||
{
|
||||
field: 'country',
|
||||
key: 'country',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.COUNTRY'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
sortBy: this.sortConfig.country || '',
|
||||
renderBodyCell: ({ row }) => {
|
||||
if (row.country) {
|
||||
@@ -182,7 +184,7 @@ export default {
|
||||
field: 'profiles',
|
||||
key: 'profiles',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.SOCIAL_PROFILES'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
renderBodyCell: ({ row }) => {
|
||||
const { profiles } = row;
|
||||
|
||||
@@ -213,14 +215,14 @@ export default {
|
||||
key: 'last_activity_at',
|
||||
sortBy: this.sortConfig.last_activity_at || '',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.LAST_ACTIVITY'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
},
|
||||
{
|
||||
field: 'created_at',
|
||||
key: 'created_at',
|
||||
sortBy: this.sortConfig.created_at || '',
|
||||
title: this.$t('CONTACTS_PAGE.LIST.TABLE_HEADER.CREATED_AT'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import NotificationPanelList from './NotificationPanelList.vue';
|
||||
|
||||
import { ACCOUNT_EVENTS } from '../../../../helper/AnalyticsHelper/events';
|
||||
@@ -9,7 +8,6 @@ export default {
|
||||
components: {
|
||||
NotificationPanelList,
|
||||
},
|
||||
mixins: [rtlMixin],
|
||||
data() {
|
||||
return {
|
||||
pageSize: 15,
|
||||
@@ -21,9 +19,6 @@ export default {
|
||||
records: 'notifications/getNotifications',
|
||||
uiFlags: 'notifications/getUIFlags',
|
||||
}),
|
||||
notificationPanelFooterIconClass() {
|
||||
return this.isRTLView ? '-mr-3' : '-ml-3';
|
||||
},
|
||||
totalUnreadNotifications() {
|
||||
return this.meta.unreadCount;
|
||||
},
|
||||
@@ -201,7 +196,7 @@ export default {
|
||||
<fluent-icon
|
||||
icon="chevron-left"
|
||||
size="16"
|
||||
:class="notificationPanelFooterIconClass"
|
||||
class="rtl:-mr-3 ltr:-ml-3"
|
||||
/>
|
||||
</woot-button>
|
||||
<woot-button
|
||||
@@ -236,7 +231,7 @@ export default {
|
||||
<fluent-icon
|
||||
icon="chevron-right"
|
||||
size="16"
|
||||
:class="notificationPanelFooterIconClass"
|
||||
class="rtl:-mr-3 ltr:-ml-3"
|
||||
/>
|
||||
</woot-button>
|
||||
</div>
|
||||
|
||||
@@ -4,14 +4,12 @@ import UserAvatarWithName from 'dashboard/components/widgets/UserAvatarWithName.
|
||||
import { CSAT_RATINGS } from 'shared/constants/messages';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { messageStamp, dynamicTime } from 'shared/helpers/timeHelper';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VeTable,
|
||||
VePagination,
|
||||
},
|
||||
mixins: [rtlMixin],
|
||||
props: {
|
||||
pageIndex: {
|
||||
type: Number,
|
||||
@@ -20,6 +18,7 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
isRTL: 'accounts/isRTL',
|
||||
csatResponses: 'csat/getCSATResponses',
|
||||
metrics: 'csat/getMetrics',
|
||||
}),
|
||||
@@ -29,7 +28,7 @@ export default {
|
||||
field: 'contact',
|
||||
key: 'contact',
|
||||
title: this.$t('CSAT_REPORTS.TABLE.HEADER.CONTACT_NAME'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
width: 200,
|
||||
renderBodyCell: ({ row }) => {
|
||||
if (row.contact) {
|
||||
@@ -48,7 +47,7 @@ export default {
|
||||
field: 'assignedAgent',
|
||||
key: 'assignedAgent',
|
||||
title: this.$t('CSAT_REPORTS.TABLE.HEADER.AGENT_NAME'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
width: 200,
|
||||
renderBodyCell: ({ row }) => {
|
||||
if (row.assignedAgent) {
|
||||
@@ -78,14 +77,14 @@ export default {
|
||||
field: 'feedbackText',
|
||||
key: 'feedbackText',
|
||||
title: this.$t('CSAT_REPORTS.TABLE.HEADER.FEEDBACK_TEXT'),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
width: 400,
|
||||
},
|
||||
{
|
||||
field: 'conversationId',
|
||||
key: 'conversationId',
|
||||
title: '',
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
width: 100,
|
||||
renderBodyCell: ({ row }) => {
|
||||
const routerParams = {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { VeTable, VePagination } from 'vue-easytable';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
import EmptyState from 'dashboard/components/widgets/EmptyState.vue';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
|
||||
export default {
|
||||
@@ -13,7 +13,6 @@ export default {
|
||||
VeTable,
|
||||
VePagination,
|
||||
},
|
||||
mixins: [rtlMixin],
|
||||
props: {
|
||||
agents: {
|
||||
type: Array,
|
||||
@@ -33,6 +32,9 @@ export default {
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
isRTL: 'accounts/isRTL',
|
||||
}),
|
||||
tableData() {
|
||||
return this.agentMetrics
|
||||
.filter(agentMetric => this.getAgentInformation(agentMetric.id))
|
||||
@@ -57,7 +59,7 @@ export default {
|
||||
'OVERVIEW_REPORTS.AGENT_CONVERSATIONS.TABLE_HEADER.AGENT'
|
||||
),
|
||||
fixed: 'left',
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
width: 25,
|
||||
renderBodyCell: ({ row }) => (
|
||||
<div class="row-user-block">
|
||||
@@ -82,7 +84,7 @@ export default {
|
||||
title: this.$t(
|
||||
'OVERVIEW_REPORTS.AGENT_CONVERSATIONS.TABLE_HEADER.OPEN'
|
||||
),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
width: 10,
|
||||
},
|
||||
{
|
||||
@@ -91,7 +93,7 @@ export default {
|
||||
title: this.$t(
|
||||
'OVERVIEW_REPORTS.AGENT_CONVERSATIONS.TABLE_HEADER.UNATTENDED'
|
||||
),
|
||||
align: this.isRTLView ? 'right' : 'left',
|
||||
align: this.isRTL ? 'right' : 'left',
|
||||
width: 10,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -4,6 +4,7 @@ import AccountAPI from '../../api/account';
|
||||
import { differenceInDays } from 'date-fns';
|
||||
import EnterpriseAccountAPI from '../../api/enterprise/account';
|
||||
import { throwErrorMessage } from '../utils/api';
|
||||
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
|
||||
const findRecordById = ($state, id) =>
|
||||
$state.records.find(record => record.id === Number(id)) || {};
|
||||
@@ -27,6 +28,13 @@ export const getters = {
|
||||
getUIFlags($state) {
|
||||
return $state.uiFlags;
|
||||
},
|
||||
isRTL: ($state, _, rootState) => {
|
||||
const accountId = rootState.route?.params?.accountId;
|
||||
if (!accountId) return false;
|
||||
|
||||
const { locale } = findRecordById($state, Number(accountId));
|
||||
return locale ? getLanguageDirection(locale) : false;
|
||||
},
|
||||
isTrialAccount: $state => id => {
|
||||
const account = findRecordById($state, id);
|
||||
const createdAt = new Date(account.created_at);
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { getters } from '../../accounts';
|
||||
import * as languageHelpers from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
|
||||
const accountData = {
|
||||
id: 1,
|
||||
@@ -46,4 +47,37 @@ describe('#getters', () => {
|
||||
)(1, 'auto_resolve_conversations')
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
describe('isRTL', () => {
|
||||
it('returns false when accountId is not present', () => {
|
||||
const rootState = { route: { params: {} } };
|
||||
expect(getters.isRTL({}, null, rootState)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for RTL language', () => {
|
||||
const state = {
|
||||
records: [{ id: 1, locale: 'ar' }],
|
||||
};
|
||||
const rootState = { route: { params: { accountId: '1' } } };
|
||||
vi.spyOn(languageHelpers, 'getLanguageDirection').mockReturnValue(true);
|
||||
expect(getters.isRTL(state, null, rootState)).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false for LTR language', () => {
|
||||
const state = {
|
||||
records: [{ id: 1, locale: 'en' }],
|
||||
};
|
||||
const rootState = { route: { params: { accountId: '1' } } };
|
||||
vi.spyOn(languageHelpers, 'getLanguageDirection').mockReturnValue(false);
|
||||
expect(getters.isRTL(state, null, rootState)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false when account is not found', () => {
|
||||
const state = {
|
||||
records: [],
|
||||
};
|
||||
const rootState = { route: { params: { accountId: '1' } } };
|
||||
expect(getters.isRTL(state, null, rootState)).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isRTLView() {
|
||||
const { rtl_view: isRTLView } = this.uiSettings;
|
||||
return isRTLView;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateRTLDirectionView(locale) {
|
||||
const isRTLSupported = getLanguageDirection(locale);
|
||||
this.updateUISettings({
|
||||
rtl_view: isRTLSupported,
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,29 +0,0 @@
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
vi.mock('dashboard/composables/useUISettings');
|
||||
|
||||
describe('rtlMixin', () => {
|
||||
const createComponent = rtl_view => {
|
||||
useUISettings.mockReturnValue({
|
||||
uiSettings: { rtl_view },
|
||||
updateUISettings: vi.fn(),
|
||||
});
|
||||
|
||||
return shallowMount({
|
||||
render() {},
|
||||
mixins: [rtlMixin],
|
||||
});
|
||||
};
|
||||
|
||||
it('returns is direction right-to-left view', () => {
|
||||
const wrapper = createComponent(true);
|
||||
expect(wrapper.vm.isRTLView).toBe(true);
|
||||
});
|
||||
|
||||
it('returns is direction left-to-right view', () => {
|
||||
const wrapper = createComponent(false);
|
||||
expect(wrapper.vm.isRTLView).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user