mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-02 12:08:01 +00:00
feat: Rewrite uiSettings mixin to a composable (#9819)
This commit is contained in:
@@ -116,6 +116,7 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import VirtualList from 'vue-virtual-scroll-list';
|
||||
|
||||
@@ -132,7 +133,6 @@ import AddCustomViews from 'dashboard/routes/dashboard/customviews/AddCustomView
|
||||
import DeleteCustomViews from 'dashboard/routes/dashboard/customviews/DeleteCustomViews.vue';
|
||||
import ConversationBulkActions from './widgets/conversation/conversationBulkActions/Index.vue';
|
||||
import filterMixin from 'shared/mixins/filterMixin';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import languages from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
import countries from 'shared/constants/countries';
|
||||
import { generateValuesForEditCustomViews } from 'dashboard/helper/customViewsHelper';
|
||||
@@ -157,12 +157,7 @@ export default {
|
||||
IntersectionObserver,
|
||||
VirtualList,
|
||||
},
|
||||
mixins: [
|
||||
conversationMixin,
|
||||
keyboardEventListenerMixins,
|
||||
filterMixin,
|
||||
uiSettingsMixin,
|
||||
],
|
||||
mixins: [conversationMixin, keyboardEventListenerMixins, filterMixin],
|
||||
provide() {
|
||||
return {
|
||||
// Actions to be performed on virtual list item and context menu.
|
||||
@@ -207,6 +202,13 @@ export default {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { uiSettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
activeAssigneeTab: wootConstants.ASSIGNEE_TYPE.ME,
|
||||
|
||||
@@ -36,12 +36,12 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAdmin } from 'dashboard/composables/useAdmin';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import AICTAModal from './AICTAModal.vue';
|
||||
import AIAssistanceModal from './AIAssistanceModal.vue';
|
||||
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||
import { CMD_AI_ASSIST } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import AIAssistanceCTAButton from './AIAssistanceCTAButton.vue';
|
||||
|
||||
export default {
|
||||
@@ -50,10 +50,14 @@ export default {
|
||||
AICTAModal,
|
||||
AIAssistanceCTAButton,
|
||||
},
|
||||
mixins: [aiMixin, keyboardEventListenerMixins, uiSettingsMixin],
|
||||
mixins: [aiMixin, keyboardEventListenerMixins],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
const { isAdmin } = useAdmin();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
isAdmin,
|
||||
};
|
||||
},
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<div class="px-0 min-w-0 flex-1">
|
||||
<div class="flex-1 min-w-0 px-0">
|
||||
<woot-modal-header
|
||||
:header-title="$t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.TITLE')"
|
||||
:header-content="$t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.DESC')"
|
||||
/>
|
||||
<form
|
||||
class="flex flex-wrap flex-col modal-content"
|
||||
class="flex flex-col flex-wrap modal-content"
|
||||
@submit.prevent="finishOpenAI"
|
||||
>
|
||||
<div class="mt-2 w-full">
|
||||
<div class="w-full mt-2">
|
||||
<woot-input
|
||||
v-model="value"
|
||||
type="text"
|
||||
@@ -19,7 +19,7 @@
|
||||
@blur="$v.value.$touch"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-row justify-between gap-2 py-2 px-0 w-full">
|
||||
<div class="flex flex-row justify-between w-full gap-2 px-0 py-2">
|
||||
<woot-button variant="link" @click.prevent="openOpenAIDoc">
|
||||
{{ $t('INTEGRATION_SETTINGS.OPEN_AI.CTA_MODAL.BUTTONS.NEED_HELP') }}
|
||||
</woot-button>
|
||||
@@ -39,13 +39,20 @@
|
||||
<script>
|
||||
import { required } from 'vuelidate/lib/validators';
|
||||
import { mapGetters } from 'vuex';
|
||||
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||
import { OPEN_AI_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
|
||||
export default {
|
||||
mixins: [aiMixin, uiSettingsMixin],
|
||||
mixins: [aiMixin],
|
||||
setup() {
|
||||
const { updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: '',
|
||||
|
||||
@@ -80,8 +80,7 @@ import {
|
||||
hasPressedCommandAndEnter,
|
||||
} from 'shared/helpers/KeyboardHelpers';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import {
|
||||
replaceVariablesInMessage,
|
||||
createTypingIndicator,
|
||||
@@ -119,7 +118,7 @@ const createState = (
|
||||
export default {
|
||||
name: 'WootMessageEditor',
|
||||
components: { TagAgents, CannedResponse, VariableList },
|
||||
mixins: [keyboardEventListenerMixins, uiSettingsMixin],
|
||||
mixins: [keyboardEventListenerMixins],
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
editorId: { type: String, default: '' },
|
||||
@@ -139,6 +138,19 @@ export default {
|
||||
channelType: { type: String, default: '' },
|
||||
showImageResizeToolbar: { type: Boolean, default: false }, // A kill switch to show or hide the image toolbar
|
||||
},
|
||||
setup() {
|
||||
const {
|
||||
uiSettings,
|
||||
isEditorHotKeyEnabled,
|
||||
fetchSignatureFlagFromUISettings,
|
||||
} = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
isEditorHotKeyEnabled,
|
||||
fetchSignatureFlagFromUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
typingIndicator: createTypingIndicator(
|
||||
@@ -278,7 +290,7 @@ export default {
|
||||
// this is considered the source of truth, we watch this property
|
||||
// on change, we toggle the signature in the editor
|
||||
if (this.allowSignature && !this.isPrivate && this.channelType) {
|
||||
return this.fetchSignatureFlagFromUiSettings(this.channelType);
|
||||
return this.fetchSignatureFlagFromUISettings(this.channelType);
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -521,10 +533,10 @@ export default {
|
||||
}
|
||||
},
|
||||
isEnterToSendEnabled() {
|
||||
return isEditorHotKeyEnabled(this.uiSettings, 'enter');
|
||||
return this.isEditorHotKeyEnabled('enter');
|
||||
},
|
||||
isCmdPlusEnterToSendEnabled() {
|
||||
return isEditorHotKeyEnabled(this.uiSettings, 'cmd_enter');
|
||||
return this.isEditorHotKeyEnabled('cmd_enter');
|
||||
},
|
||||
getKeyboardEvents() {
|
||||
return {
|
||||
|
||||
@@ -25,8 +25,8 @@ import {
|
||||
} from '@chatwoot/prosemirror-schema';
|
||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
const MAXIMUM_FILE_UPLOAD_SIZE = 4; // in MB
|
||||
const createState = (
|
||||
@@ -51,13 +51,21 @@ const createState = (
|
||||
};
|
||||
|
||||
export default {
|
||||
mixins: [keyboardEventListenerMixins, uiSettingsMixin],
|
||||
mixins: [keyboardEventListenerMixins],
|
||||
props: {
|
||||
value: { type: String, default: '' },
|
||||
editorId: { type: String, default: '' },
|
||||
placeholder: { type: String, default: '' },
|
||||
enabledMenuOptions: { type: Array, default: () => [] },
|
||||
},
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editorView: null,
|
||||
|
||||
@@ -134,10 +134,10 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import FileUpload from 'vue-upload-component';
|
||||
import * as ActiveStorage from 'activestorage';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import inboxMixin from 'shared/mixins/inboxMixin';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
import {
|
||||
@@ -153,7 +153,7 @@ import { mapGetters } from 'vuex';
|
||||
export default {
|
||||
name: 'ReplyBottomPanel',
|
||||
components: { FileUpload, VideoCallButton, AIAssistanceButton },
|
||||
mixins: [keyboardEventListenerMixins, uiSettingsMixin, inboxMixin],
|
||||
mixins: [keyboardEventListenerMixins, inboxMixin],
|
||||
props: {
|
||||
mode: {
|
||||
type: String,
|
||||
@@ -248,6 +248,15 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { setSignatureFlagForInbox, fetchSignatureFlagFromUISettings } =
|
||||
useUISettings();
|
||||
|
||||
return {
|
||||
setSignatureFlagForInbox,
|
||||
fetchSignatureFlagFromUISettings,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
accountId: 'getCurrentAccountId',
|
||||
@@ -320,7 +329,7 @@ export default {
|
||||
},
|
||||
sendWithSignature() {
|
||||
// channelType is sourced from inboxMixin
|
||||
return this.fetchSignatureFlagFromUiSettings(this.channelType);
|
||||
return this.fetchSignatureFlagFromUISettings(this.channelType);
|
||||
},
|
||||
signatureToggleTooltip() {
|
||||
return this.sendWithSignature
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
<div
|
||||
v-if="showActionsDropdown"
|
||||
v-on-clickaway="closeDropdown"
|
||||
class="dropdown-pane dropdown-pane--open mt-1 right-0 basic-filter"
|
||||
class="right-0 mt-1 dropdown-pane dropdown-pane--open basic-filter"
|
||||
>
|
||||
<div class="items-center flex justify-between last:mt-4">
|
||||
<span class="text-slate-800 dark:text-slate-100 text-xs font-medium">{{
|
||||
<div class="flex items-center justify-between last:mt-4">
|
||||
<span class="text-xs font-medium text-slate-800 dark:text-slate-100">{{
|
||||
$t('CHAT_LIST.CHAT_SORT.STATUS')
|
||||
}}</span>
|
||||
<filter-item
|
||||
@@ -26,8 +26,8 @@
|
||||
@onChangeFilter="onChangeFilter"
|
||||
/>
|
||||
</div>
|
||||
<div class="items-center flex justify-between last:mt-4">
|
||||
<span class="text-slate-800 dark:text-slate-100 text-xs font-medium">{{
|
||||
<div class="flex items-center justify-between last:mt-4">
|
||||
<span class="text-xs font-medium text-slate-800 dark:text-slate-100">{{
|
||||
$t('CHAT_LIST.CHAT_SORT.ORDER_BY')
|
||||
}}</span>
|
||||
<filter-item
|
||||
@@ -46,13 +46,19 @@
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { mapGetters } from 'vuex';
|
||||
import FilterItem from './FilterItem.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FilterItem,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
setup() {
|
||||
const { updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showActionsDropdown: false,
|
||||
|
||||
@@ -154,6 +154,7 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
|
||||
import CannedResponse from './CannedResponse.vue';
|
||||
@@ -180,10 +181,8 @@ import {
|
||||
import WhatsappTemplates from './WhatsappTemplates/Modal.vue';
|
||||
import { MESSAGE_MAX_LENGTH } from 'shared/helpers/MessageTypeHelper';
|
||||
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { trimContent, debounce } from '@chatwoot/utils';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { isEditorHotKeyEnabled } from 'dashboard/mixins/uiSettings';
|
||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||
import rtlMixin from 'shared/mixins/rtlMixin';
|
||||
import fileUploadMixin from 'dashboard/mixins/fileUploadMixin';
|
||||
@@ -218,7 +217,6 @@ export default {
|
||||
},
|
||||
mixins: [
|
||||
inboxMixin,
|
||||
uiSettingsMixin,
|
||||
messageFormatterMixin,
|
||||
rtlMixin,
|
||||
fileUploadMixin,
|
||||
@@ -230,6 +228,21 @@ export default {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
isEditorHotKeyEnabled,
|
||||
fetchSignatureFlagFromUISettings,
|
||||
} = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
isEditorHotKeyEnabled,
|
||||
fetchSignatureFlagFromUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
message: '',
|
||||
@@ -405,7 +418,7 @@ export default {
|
||||
if (this.isPrivate) {
|
||||
sendMessageText = this.$t('CONVERSATION.REPLYBOX.CREATE');
|
||||
}
|
||||
const keyLabel = isEditorHotKeyEnabled(this.uiSettings, 'cmd_enter')
|
||||
const keyLabel = this.isEditorHotKeyEnabled('cmd_enter')
|
||||
? '(⌘ + ↵)'
|
||||
: '(↵)';
|
||||
return `${sendMessageText} ${keyLabel}`;
|
||||
@@ -478,7 +491,7 @@ export default {
|
||||
return !!this.signatureToApply;
|
||||
},
|
||||
sendWithSignature() {
|
||||
return this.fetchSignatureFlagFromUiSettings(this.channelType);
|
||||
return this.fetchSignatureFlagFromUISettings(this.channelType);
|
||||
},
|
||||
editorMessageKey() {
|
||||
const { editor_message_key: isEnabled } = this.uiSettings;
|
||||
@@ -747,7 +760,7 @@ export default {
|
||||
!this.showCannedMenu &&
|
||||
!this.showVariablesMenu &&
|
||||
this.isFocused &&
|
||||
isEditorHotKeyEnabled(this.uiSettings, selectedKey)
|
||||
this.isEditorHotKeyEnabled(selectedKey)
|
||||
);
|
||||
},
|
||||
onPaste(e) {
|
||||
|
||||
138
app/javascript/dashboard/composables/spec/useUISettings.spec.js
Normal file
138
app/javascript/dashboard/composables/spec/useUISettings.spec.js
Normal file
@@ -0,0 +1,138 @@
|
||||
import { ref } from 'vue';
|
||||
import {
|
||||
useUISettings,
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
} from 'dashboard/composables/useUISettings';
|
||||
|
||||
// Mocking the store composables
|
||||
const mockDispatch = vi.fn();
|
||||
|
||||
const getUISettingsMock = ref({
|
||||
is_ct_labels_open: true,
|
||||
conversation_sidebar_items_order: DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
editor_message_key: 'enter',
|
||||
});
|
||||
|
||||
vi.mock('dashboard/composables/store', () => ({
|
||||
useStoreGetters: () => ({
|
||||
getUISettings: getUISettingsMock,
|
||||
}),
|
||||
useStore: () => ({
|
||||
dispatch: mockDispatch,
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('useUISettings', () => {
|
||||
beforeEach(() => {
|
||||
mockDispatch.mockClear();
|
||||
});
|
||||
|
||||
it('returns uiSettings', () => {
|
||||
const { uiSettings } = useUISettings();
|
||||
expect(uiSettings.value).toEqual({
|
||||
is_ct_labels_open: true,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
editor_message_key: 'enter',
|
||||
});
|
||||
});
|
||||
|
||||
it('updates UI settings correctly', () => {
|
||||
const { updateUISettings } = useUISettings();
|
||||
updateUISettings({ enter_to_send_enabled: true });
|
||||
expect(mockDispatch).toHaveBeenCalledWith('updateUISettings', {
|
||||
uiSettings: {
|
||||
enter_to_send_enabled: true,
|
||||
is_ct_labels_open: true,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
editor_message_key: 'enter',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('toggles sidebar UI state correctly', () => {
|
||||
const { toggleSidebarUIState } = useUISettings();
|
||||
toggleSidebarUIState('is_ct_labels_open');
|
||||
expect(mockDispatch).toHaveBeenCalledWith('updateUISettings', {
|
||||
uiSettings: {
|
||||
is_ct_labels_open: false,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
editor_message_key: 'enter',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('returns correct conversation sidebar items order', () => {
|
||||
const { conversationSidebarItemsOrder } = useUISettings();
|
||||
expect(conversationSidebarItemsOrder.value).toEqual(
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER
|
||||
);
|
||||
});
|
||||
|
||||
it('returns correct contact sidebar items order', () => {
|
||||
const { contactSidebarItemsOrder } = useUISettings();
|
||||
expect(contactSidebarItemsOrder.value).toEqual(
|
||||
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER
|
||||
);
|
||||
});
|
||||
|
||||
it('returns correct value for isContactSidebarItemOpen', () => {
|
||||
const { isContactSidebarItemOpen } = useUISettings();
|
||||
expect(isContactSidebarItemOpen('is_ct_labels_open')).toBe(true);
|
||||
expect(isContactSidebarItemOpen('non_existent_key')).toBe(false);
|
||||
});
|
||||
|
||||
it('sets signature flag for inbox correctly', () => {
|
||||
const { setSignatureFlagForInbox } = useUISettings();
|
||||
setSignatureFlagForInbox('email', true);
|
||||
expect(mockDispatch).toHaveBeenCalledWith('updateUISettings', {
|
||||
uiSettings: {
|
||||
is_ct_labels_open: true,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
email_signature_enabled: true,
|
||||
editor_message_key: 'enter',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('fetches signature flag from UI settings correctly', () => {
|
||||
const { fetchSignatureFlagFromUISettings } = useUISettings();
|
||||
expect(fetchSignatureFlagFromUISettings('email')).toBe(undefined);
|
||||
});
|
||||
|
||||
it('returns correct value for isEditorHotKeyEnabled when editor_message_key is configured', () => {
|
||||
getUISettingsMock.value.enter_to_send_enabled = false;
|
||||
const { isEditorHotKeyEnabled } = useUISettings();
|
||||
expect(isEditorHotKeyEnabled('enter')).toBe(true);
|
||||
expect(isEditorHotKeyEnabled('cmd_enter')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns correct value for isEditorHotKeyEnabled when editor_message_key is not configured', () => {
|
||||
getUISettingsMock.value.editor_message_key = undefined;
|
||||
const { isEditorHotKeyEnabled } = useUISettings();
|
||||
expect(isEditorHotKeyEnabled('enter')).toBe(false);
|
||||
expect(isEditorHotKeyEnabled('cmd_enter')).toBe(true);
|
||||
});
|
||||
|
||||
it('handles non-existent keys', () => {
|
||||
const {
|
||||
isContactSidebarItemOpen,
|
||||
fetchSignatureFlagFromUISettings,
|
||||
isEditorHotKeyEnabled,
|
||||
} = useUISettings();
|
||||
expect(isContactSidebarItemOpen('non_existent_key')).toBe(false);
|
||||
expect(fetchSignatureFlagFromUISettings('non_existent_key')).toBe(
|
||||
undefined
|
||||
);
|
||||
expect(isEditorHotKeyEnabled('non_existent_key')).toBe(false);
|
||||
});
|
||||
});
|
||||
149
app/javascript/dashboard/composables/useUISettings.js
Normal file
149
app/javascript/dashboard/composables/useUISettings.js
Normal file
@@ -0,0 +1,149 @@
|
||||
import { computed } from 'vue';
|
||||
import { useStore, useStoreGetters } from 'dashboard/composables/store';
|
||||
|
||||
export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = Object.freeze([
|
||||
{ name: 'conversation_actions' },
|
||||
{ name: 'macros' },
|
||||
{ name: 'conversation_info' },
|
||||
{ name: 'contact_attributes' },
|
||||
{ name: 'previous_conversation' },
|
||||
{ name: 'conversation_participants' },
|
||||
]);
|
||||
|
||||
export const DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER = Object.freeze([
|
||||
{ name: 'contact_attributes' },
|
||||
{ name: 'contact_labels' },
|
||||
{ name: 'previous_conversation' },
|
||||
]);
|
||||
|
||||
/**
|
||||
* Slugifies the channel name.
|
||||
* Replaces spaces, hyphens, and double colons with underscores.
|
||||
* @param {string} name - The channel name to slugify.
|
||||
* @returns {string} The slugified channel name.
|
||||
*/
|
||||
const slugifyChannel = name =>
|
||||
name?.toLowerCase().replace(' ', '_').replace('-', '_').replace('::', '_');
|
||||
|
||||
/**
|
||||
* Computes the order of items in the conversation sidebar, using defaults if not present.
|
||||
* @param {Object} uiSettings - Reactive UI settings object.
|
||||
* @returns {Array} Ordered list of sidebar items.
|
||||
*/
|
||||
const useConversationSidebarItemsOrder = uiSettings => {
|
||||
return computed(() => {
|
||||
const { conversation_sidebar_items_order: itemsOrder } = uiSettings.value;
|
||||
// If the sidebar order is not set, use the default order.
|
||||
if (!itemsOrder) {
|
||||
return DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER;
|
||||
}
|
||||
// Create a copy of itemsOrder to avoid mutating the original store object.
|
||||
const itemsOrderCopy = [...itemsOrder];
|
||||
// If the sidebar order doesn't have the new elements, then add them to the list.
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER.forEach(item => {
|
||||
if (!itemsOrderCopy.find(i => i.name === item.name)) {
|
||||
itemsOrderCopy.push(item);
|
||||
}
|
||||
});
|
||||
return itemsOrderCopy;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes the order of items in the contact sidebar,using defaults if not present.
|
||||
* @param {Object} uiSettings - Reactive UI settings object.
|
||||
* @returns {Array} Ordered list of sidebar items.
|
||||
*/
|
||||
const useContactSidebarItemsOrder = uiSettings => {
|
||||
return computed(() => {
|
||||
const { contact_sidebar_items_order: itemsOrder } = uiSettings.value;
|
||||
return itemsOrder || DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles the open state of a sidebar item.
|
||||
* @param {string} key - The key of the sidebar item to toggle.
|
||||
* @param {Object} uiSettings - Reactive UI settings object.
|
||||
* @param {Function} updateUISettings - Function to update UI settings.
|
||||
*/
|
||||
const toggleSidebarUIState = (key, uiSettings, updateUISettings) => {
|
||||
updateUISettings({ [key]: !uiSettings.value[key] });
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the signature flag for a specific channel type in the inbox settings.
|
||||
* @param {string} channelType - The type of the channel.
|
||||
* @param {boolean} value - The value to set for the signature enabled flag.
|
||||
* @param {Function} updateUISettings - Function to update UI settings.
|
||||
*/
|
||||
const setSignatureFlagForInbox = (channelType, value, updateUISettings) => {
|
||||
if (!channelType) return;
|
||||
|
||||
const slugifiedChannel = slugifyChannel(channelType);
|
||||
updateUISettings({ [`${slugifiedChannel}_signature_enabled`]: value });
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the signature flag for a specific channel type from UI settings.
|
||||
* @param {string} channelType - The type of the channel.
|
||||
* @param {Object} uiSettings - Reactive UI settings object.
|
||||
* @returns {boolean} The value of the signature enabled flag.
|
||||
*/
|
||||
const fetchSignatureFlagFromUISettings = (channelType, uiSettings) => {
|
||||
if (!channelType) return false;
|
||||
|
||||
const slugifiedChannel = slugifyChannel(channelType);
|
||||
return uiSettings.value[`${slugifiedChannel}_signature_enabled`];
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a specific editor hotkey is enabled.
|
||||
* @param {string} key - The key to check.
|
||||
* @param {Object} uiSettings - Reactive UI settings object.
|
||||
* @returns {boolean} True if the hotkey is enabled, otherwise false.
|
||||
*/
|
||||
const isEditorHotKeyEnabled = (key, uiSettings) => {
|
||||
const {
|
||||
editor_message_key: editorMessageKey,
|
||||
enter_to_send_enabled: enterToSendEnabled,
|
||||
} = uiSettings.value || {};
|
||||
if (!editorMessageKey) {
|
||||
return key === (enterToSendEnabled ? 'enter' : 'cmd_enter');
|
||||
}
|
||||
return editorMessageKey === key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Main composable function for managing UI settings.
|
||||
* @returns {Object} An object containing reactive properties and methods for UI settings management.
|
||||
*/
|
||||
export function useUISettings() {
|
||||
const getters = useStoreGetters();
|
||||
const store = useStore();
|
||||
const uiSettings = computed(() => getters.getUISettings.value);
|
||||
|
||||
const updateUISettings = (settings = {}) => {
|
||||
store.dispatch('updateUISettings', {
|
||||
uiSettings: {
|
||||
...uiSettings.value,
|
||||
...settings,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
conversationSidebarItemsOrder: useConversationSidebarItemsOrder(uiSettings),
|
||||
contactSidebarItemsOrder: useContactSidebarItemsOrder(uiSettings),
|
||||
isContactSidebarItemOpen: key => !!uiSettings.value[key],
|
||||
toggleSidebarUIState: key =>
|
||||
toggleSidebarUIState(key, uiSettings, updateUISettings),
|
||||
setSignatureFlagForInbox: (channelType, value) =>
|
||||
setSignatureFlagForInbox(channelType, value, updateUISettings),
|
||||
fetchSignatureFlagFromUISettings: channelType =>
|
||||
fetchSignatureFlagFromUISettings(channelType, uiSettings),
|
||||
isEditorHotKeyEnabled: key => isEditorHotKeyEnabled(key, uiSettings),
|
||||
};
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
import { shallowMount, createLocalVue } from '@vue/test-utils';
|
||||
import uiSettingsMixin, {
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
isEditorHotKeyEnabled,
|
||||
} from '../uiSettings';
|
||||
import Vuex from 'vuex';
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
|
||||
describe('uiSettingsMixin', () => {
|
||||
let getters;
|
||||
let actions;
|
||||
let store;
|
||||
|
||||
beforeEach(() => {
|
||||
actions = { updateUISettings: vi.fn(), toggleSidebarUIState: vi.fn() };
|
||||
getters = {
|
||||
getUISettings: () => ({
|
||||
enter_to_send_enabled: false,
|
||||
is_ct_labels_open: true,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
}),
|
||||
};
|
||||
store = new Vuex.Store({ actions, getters });
|
||||
});
|
||||
|
||||
it('returns uiSettings', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
expect(wrapper.vm.uiSettings).toEqual({
|
||||
enter_to_send_enabled: false,
|
||||
is_ct_labels_open: true,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateUISettings', () => {
|
||||
it('dispatches store actions correctly', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
wrapper.vm.updateUISettings({ enter_to_send_enabled: true });
|
||||
expect(actions.updateUISettings).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{
|
||||
uiSettings: {
|
||||
enter_to_send_enabled: true,
|
||||
is_ct_labels_open: true,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
},
|
||||
},
|
||||
undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#toggleSidebarUIState', () => {
|
||||
it('dispatches store actions correctly', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
wrapper.vm.toggleSidebarUIState('is_ct_labels_open');
|
||||
expect(actions.updateUISettings).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
{
|
||||
uiSettings: {
|
||||
enter_to_send_enabled: false,
|
||||
is_ct_labels_open: false,
|
||||
conversation_sidebar_items_order:
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER,
|
||||
contact_sidebar_items_order: DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER,
|
||||
},
|
||||
},
|
||||
undefined
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#isContactSidebarItemOpen', () => {
|
||||
it('returns correct values', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
expect(wrapper.vm.isContactSidebarItemOpen('is_ct_labels_open')).toEqual(
|
||||
true
|
||||
);
|
||||
expect(
|
||||
wrapper.vm.isContactSidebarItemOpen('is_ct_prev_conv_open')
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#conversationSidebarItemsOrder', () => {
|
||||
it('returns correct values', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
expect(wrapper.vm.conversationSidebarItemsOrder).toEqual([
|
||||
{ name: 'conversation_actions' },
|
||||
{ name: 'macros' },
|
||||
{ name: 'conversation_info' },
|
||||
{ name: 'contact_attributes' },
|
||||
{ name: 'previous_conversation' },
|
||||
{ name: 'conversation_participants' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
describe('#contactSidebarItemsOrder', () => {
|
||||
it('returns correct values', () => {
|
||||
const Component = {
|
||||
render() {},
|
||||
title: 'TestComponent',
|
||||
mixins: [uiSettingsMixin],
|
||||
};
|
||||
const wrapper = shallowMount(Component, { store, localVue });
|
||||
expect(wrapper.vm.contactSidebarItemsOrder).toEqual([
|
||||
{ name: 'contact_attributes' },
|
||||
{ name: 'contact_labels' },
|
||||
{ name: 'previous_conversation' },
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('isEditorHotKeyEnabled', () => {
|
||||
it('returns true if hot key is not configured and enter to send flag is true', () => {
|
||||
expect(
|
||||
isEditorHotKeyEnabled({ enter_to_send_enabled: true }, 'enter')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
isEditorHotKeyEnabled({ enter_to_send_enabled: true }, 'cmd_enter')
|
||||
).toEqual(false);
|
||||
|
||||
expect(isEditorHotKeyEnabled({}, 'cmd_enter')).toEqual(true);
|
||||
expect(isEditorHotKeyEnabled({}, 'enter')).toEqual(false);
|
||||
});
|
||||
|
||||
it('returns correct value if hot key is configured', () => {
|
||||
expect(
|
||||
isEditorHotKeyEnabled({ editor_message_key: 'enter' }, 'enter')
|
||||
).toEqual(true);
|
||||
expect(
|
||||
isEditorHotKeyEnabled({ editor_message_key: 'cmd_enter' }, 'enter')
|
||||
).toEqual(false);
|
||||
});
|
||||
});
|
||||
@@ -1,87 +0,0 @@
|
||||
import { mapGetters } from 'vuex';
|
||||
|
||||
export const DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER = [
|
||||
{ name: 'conversation_actions' },
|
||||
{ name: 'macros' },
|
||||
{ name: 'conversation_info' },
|
||||
{ name: 'contact_attributes' },
|
||||
{ name: 'previous_conversation' },
|
||||
{ name: 'conversation_participants' },
|
||||
];
|
||||
export const DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER = [
|
||||
{ name: 'contact_attributes' },
|
||||
{ name: 'contact_labels' },
|
||||
{ name: 'previous_conversation' },
|
||||
];
|
||||
|
||||
const slugifyChannel = name =>
|
||||
name?.toLowerCase().replace(' ', '_').replace('-', '_').replace('::', '_');
|
||||
|
||||
export const isEditorHotKeyEnabled = (uiSettings, key) => {
|
||||
const {
|
||||
editor_message_key: editorMessageKey,
|
||||
enter_to_send_enabled: enterToSendEnabled,
|
||||
} = uiSettings || {};
|
||||
if (!editorMessageKey) {
|
||||
if (enterToSendEnabled) {
|
||||
return key === 'enter';
|
||||
}
|
||||
return key === 'cmd_enter';
|
||||
}
|
||||
return editorMessageKey === key;
|
||||
};
|
||||
|
||||
export default {
|
||||
computed: {
|
||||
...mapGetters({ uiSettings: 'getUISettings' }),
|
||||
conversationSidebarItemsOrder() {
|
||||
const { conversation_sidebar_items_order: itemsOrder } = this.uiSettings;
|
||||
// If the sidebar order is not set, use the default order.
|
||||
if (!itemsOrder) {
|
||||
return DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER;
|
||||
}
|
||||
// If the sidebar order doesn't have the new elements, then add them to the list.
|
||||
DEFAULT_CONVERSATION_SIDEBAR_ITEMS_ORDER.forEach(item => {
|
||||
if (!itemsOrder.find(i => i.name === item.name)) {
|
||||
itemsOrder.push(item);
|
||||
}
|
||||
});
|
||||
return itemsOrder;
|
||||
},
|
||||
contactSidebarItemsOrder() {
|
||||
const { contact_sidebar_items_order: itemsOrder } = this.uiSettings;
|
||||
return itemsOrder || DEFAULT_CONTACT_SIDEBAR_ITEMS_ORDER;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
updateUISettings(uiSettings = {}) {
|
||||
this.$store.dispatch('updateUISettings', {
|
||||
uiSettings: {
|
||||
...this.uiSettings,
|
||||
...uiSettings,
|
||||
},
|
||||
});
|
||||
},
|
||||
isContactSidebarItemOpen(key) {
|
||||
const { [key]: isOpen } = this.uiSettings;
|
||||
return !!isOpen;
|
||||
},
|
||||
toggleSidebarUIState(key) {
|
||||
this.updateUISettings({ [key]: !this.isContactSidebarItemOpen(key) });
|
||||
},
|
||||
setSignatureFlagForInbox(channelType, value) {
|
||||
if (!channelType) return;
|
||||
|
||||
channelType = slugifyChannel(channelType);
|
||||
this.updateUISettings({
|
||||
[`${channelType}_signature_enabled`]: value,
|
||||
});
|
||||
},
|
||||
fetchSignatureFlagFromUiSettings(channelType) {
|
||||
if (!channelType) return false;
|
||||
|
||||
channelType = slugifyChannel(channelType);
|
||||
return this.uiSettings[`${channelType}_signature_enabled`];
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="app-wrapper h-full flex-grow-0 min-h-0 w-full max-w-full ml-auto mr-auto flex flex-wrap dark:text-slate-300"
|
||||
class="flex flex-wrap flex-grow-0 w-full h-full max-w-full min-h-0 ml-auto mr-auto app-wrapper dark:text-slate-300"
|
||||
>
|
||||
<sidebar
|
||||
:route="currentRoute"
|
||||
@@ -11,7 +11,7 @@
|
||||
@close-key-shortcut-modal="closeKeyShortcutModal"
|
||||
@show-add-label-popup="showAddLabelPopup"
|
||||
/>
|
||||
<section class="flex h-full min-h-0 overflow-hidden flex-1 px-0">
|
||||
<section class="flex flex-1 h-full min-h-0 px-0 overflow-hidden">
|
||||
<router-view />
|
||||
<command-bar />
|
||||
<account-selector
|
||||
@@ -47,7 +47,7 @@ import AddAccountModal from 'dashboard/components/layout/sidebarComponents/AddAc
|
||||
import AccountSelector from 'dashboard/components/layout/sidebarComponents/AccountSelector.vue';
|
||||
import AddLabelModal from 'dashboard/routes/dashboard/settings/labels/AddLabel.vue';
|
||||
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
const CommandBar = () => import('./commands/commandbar.vue');
|
||||
|
||||
@@ -61,7 +61,14 @@ export default {
|
||||
AddLabelModal,
|
||||
NotificationPanel,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAccountModal: false,
|
||||
|
||||
@@ -86,7 +86,7 @@ import ContactInfo from 'dashboard/routes/dashboard/conversation/contact/Contact
|
||||
import ContactLabel from 'dashboard/routes/dashboard/contacts/components/ContactLabels.vue';
|
||||
import CustomAttributes from 'dashboard/routes/dashboard/conversation/customAttributes/CustomAttributes.vue';
|
||||
import draggable from 'vuedraggable';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -97,7 +97,6 @@ export default {
|
||||
CustomAttributes,
|
||||
draggable,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
props: {
|
||||
contact: {
|
||||
type: Object,
|
||||
@@ -116,6 +115,21 @@ export default {
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const {
|
||||
updateUISettings,
|
||||
isContactSidebarItemOpen,
|
||||
contactSidebarItemsOrder,
|
||||
toggleSidebarUIState,
|
||||
} = useUISettings();
|
||||
|
||||
return {
|
||||
updateUISettings,
|
||||
isContactSidebarItemOpen,
|
||||
contactSidebarItemsOrder,
|
||||
toggleSidebarUIState,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragEnabled: true,
|
||||
|
||||
@@ -133,6 +133,7 @@
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import AccordionItem from 'dashboard/components/Accordion/AccordionItem.vue';
|
||||
import ContactConversations from './ContactConversations.vue';
|
||||
import ConversationAction from './ConversationAction.vue';
|
||||
@@ -142,7 +143,6 @@ import ContactInfo from './contact/ContactInfo.vue';
|
||||
import ConversationInfo from './ConversationInfo.vue';
|
||||
import CustomAttributes from './customAttributes/CustomAttributes.vue';
|
||||
import draggable from 'vuedraggable';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import MacrosList from './Macros/List.vue';
|
||||
export default {
|
||||
components: {
|
||||
@@ -156,7 +156,6 @@ export default {
|
||||
draggable,
|
||||
MacrosList,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
props: {
|
||||
conversationId: {
|
||||
type: [Number, String],
|
||||
@@ -171,6 +170,21 @@ export default {
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const {
|
||||
updateUISettings,
|
||||
isContactSidebarItemOpen,
|
||||
conversationSidebarItemsOrder,
|
||||
toggleSidebarUIState,
|
||||
} = useUISettings();
|
||||
|
||||
return {
|
||||
updateUISettings,
|
||||
isContactSidebarItemOpen,
|
||||
conversationSidebarItemsOrder,
|
||||
toggleSidebarUIState,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dragEnabled: true,
|
||||
|
||||
@@ -37,12 +37,12 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { getUnixTime } from 'date-fns';
|
||||
import ChatList from '../../../components/ChatList.vue';
|
||||
import ConversationBox from '../../../components/widgets/conversation/ConversationBox.vue';
|
||||
import PopOverSearch from './search/PopOverSearch.vue';
|
||||
import CustomSnoozeModal from 'dashboard/components/CustomSnoozeModal.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import { CMD_SNOOZE_CONVERSATION } from 'dashboard/routes/dashboard/commands/commandBarBusEvents';
|
||||
@@ -55,7 +55,6 @@ export default {
|
||||
PopOverSearch,
|
||||
CustomSnoozeModal,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
props: {
|
||||
inboxId: {
|
||||
type: [String, Number],
|
||||
@@ -82,6 +81,14 @@ export default {
|
||||
default: 0,
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSearchModal: false,
|
||||
|
||||
@@ -240,6 +240,7 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||
import ReplyEmailHead from 'dashboard/components/widgets/conversation/ReplyEmailHead.vue';
|
||||
@@ -260,7 +261,6 @@ import {
|
||||
appendSignature,
|
||||
removeSignature,
|
||||
} from 'dashboard/helper/editorHelper';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -274,7 +274,7 @@ export default {
|
||||
AttachmentPreview,
|
||||
MessageSignatureMissingAlert,
|
||||
},
|
||||
mixins: [uiSettingsMixin, inboxMixin, fileUploadMixin],
|
||||
mixins: [inboxMixin, fileUploadMixin],
|
||||
props: {
|
||||
contact: {
|
||||
type: Object,
|
||||
@@ -289,6 +289,15 @@ export default {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { fetchSignatureFlagFromUISettings, setSignatureFlagForInbox } =
|
||||
useUISettings();
|
||||
|
||||
return {
|
||||
fetchSignatureFlagFromUISettings,
|
||||
setSignatureFlagForInbox,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
name: '',
|
||||
@@ -323,7 +332,7 @@ export default {
|
||||
messageSignature: 'getMessageSignature',
|
||||
}),
|
||||
sendWithSignature() {
|
||||
return this.fetchSignatureFlagFromUiSettings(this.channelType);
|
||||
return this.fetchSignatureFlagFromUISettings(this.channelType);
|
||||
},
|
||||
signatureToApply() {
|
||||
return this.messageSignature;
|
||||
|
||||
@@ -41,17 +41,17 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import CustomAttribute from 'dashboard/components/CustomAttribute.vue';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import attributeMixin from 'dashboard/mixins/attributeMixin';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
||||
import CustomAttribute from 'dashboard/components/CustomAttribute.vue';
|
||||
import attributeMixin from 'dashboard/mixins/attributeMixin';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CustomAttribute,
|
||||
},
|
||||
mixins: [attributeMixin, uiSettingsMixin],
|
||||
mixins: [attributeMixin],
|
||||
props: {
|
||||
attributeType: {
|
||||
type: String,
|
||||
@@ -71,6 +71,14 @@ export default {
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAllAttributes: false,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="app-wrapper flex h-full flex-grow-0 min-h-0 w-full">
|
||||
<div class="flex flex-grow-0 w-full h-full min-h-0 app-wrapper">
|
||||
<sidebar
|
||||
:route="currentRoute"
|
||||
@toggle-account-modal="toggleAccountModal"
|
||||
@@ -20,7 +20,7 @@
|
||||
/>
|
||||
<section
|
||||
v-if="isHelpCenterEnabled"
|
||||
class="flex h-full min-h-0 overflow-hidden flex-1 px-0 bg-white dark:bg-slate-900"
|
||||
class="flex flex-1 h-full min-h-0 px-0 overflow-hidden bg-white dark:bg-slate-900"
|
||||
>
|
||||
<router-view @reload-locale="fetchPortalAndItsCategories" />
|
||||
<command-bar />
|
||||
@@ -68,7 +68,7 @@ import HelpCenterSidebar from '../components/Sidebar/Sidebar.vue';
|
||||
import WootKeyShortcutModal from 'dashboard/components/widgets/modal/WootKeyShortcutModal.vue';
|
||||
import AccountSelector from 'dashboard/components/layout/sidebarComponents/AccountSelector.vue';
|
||||
import NotificationPanel from 'dashboard/routes/dashboard/notifications/components/NotificationPanel.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import portalMixin from '../mixins/portalMixin';
|
||||
import AddCategory from '../pages/categories/AddCategory.vue';
|
||||
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||
@@ -87,7 +87,15 @@ export default {
|
||||
UpgradePage,
|
||||
WootKeyShortcutModal,
|
||||
},
|
||||
mixins: [portalMixin, uiSettingsMixin],
|
||||
mixins: [portalMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isOnDesktop: true,
|
||||
|
||||
@@ -191,9 +191,9 @@
|
||||
|
||||
<script>
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
import LocaleItemTable from './PortalListItemTable.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { PORTALS_EVENTS } from '../../../../helper/AnalyticsHelper/events';
|
||||
|
||||
export default {
|
||||
@@ -201,7 +201,6 @@ export default {
|
||||
thumbnail,
|
||||
LocaleItemTable,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
props: {
|
||||
portal: {
|
||||
type: Object,
|
||||
@@ -213,6 +212,13 @@ export default {
|
||||
values: ['archived', 'draft', 'published'],
|
||||
},
|
||||
},
|
||||
setup() {
|
||||
const { updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showDeleteConfirmationPopup: false,
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
<template>
|
||||
<div
|
||||
class="text-slate-600 dark:text-slate-200 flex items-center justify-center w-full"
|
||||
class="flex items-center justify-center w-full text-slate-600 dark:text-slate-200"
|
||||
>
|
||||
Loading...
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
export default {
|
||||
mixins: [uiSettingsMixin],
|
||||
setup() {
|
||||
const { uiSettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({ portals: 'portals/allPortals' }),
|
||||
},
|
||||
|
||||
@@ -47,13 +47,13 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
|
||||
import InboxCard from './components/InboxCard.vue';
|
||||
import InboxListHeader from './components/InboxListHeader.vue';
|
||||
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import IntersectionObserver from 'dashboard/components/IntersectionObserver.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -61,7 +61,13 @@ export default {
|
||||
InboxListHeader,
|
||||
IntersectionObserver,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
setup() {
|
||||
const { uiSettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
infiniteLoaderOptions: {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
:empty-state-message="$t('INBOX.LIST.NO_MESSAGES_AVAILABLE')"
|
||||
/>
|
||||
</div>
|
||||
<div v-else class="flex flex-col h-full w-full">
|
||||
<div v-else class="flex flex-col w-full h-full">
|
||||
<inbox-item-header
|
||||
class="flex-1"
|
||||
:total-length="totalNotificationCount"
|
||||
@@ -18,7 +18,7 @@
|
||||
v-if="isConversationLoading"
|
||||
class="flex items-center h-[calc(100%-56px)] justify-center bg-slate-25 dark:bg-slate-800"
|
||||
>
|
||||
<span class="spinner my-4" />
|
||||
<span class="my-4 spinner" />
|
||||
</div>
|
||||
<conversation-box
|
||||
v-else
|
||||
@@ -38,7 +38,7 @@ import { mapGetters } from 'vuex';
|
||||
import InboxItemHeader from './components/InboxItemHeader.vue';
|
||||
import ConversationBox from 'dashboard/components/widgets/conversation/ConversationBox.vue';
|
||||
import InboxEmptyState from './InboxEmptyState.vue';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import { INBOX_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
|
||||
@@ -48,7 +48,14 @@ export default {
|
||||
InboxEmptyState,
|
||||
ConversationBox,
|
||||
},
|
||||
mixins: [uiSettingsMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
isConversationLoading: false,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
class="flex flex-col bg-white z-50 dark:bg-slate-900 w-[170px] border shadow-md border-slate-100 dark:border-slate-700/50 rounded-xl divide-y divide-slate-100 dark:divide-slate-700/50"
|
||||
>
|
||||
<div class="flex items-center justify-between h-11 p-3 rounded-t-lg">
|
||||
<div class="flex items-center justify-between p-3 rounded-t-lg h-11">
|
||||
<div class="flex gap-1.5">
|
||||
<fluent-icon
|
||||
icon="arrow-sort"
|
||||
@@ -10,7 +10,7 @@
|
||||
size="16"
|
||||
class="text-slate-700 dark:text-slate-100"
|
||||
/>
|
||||
<span class="font-medium text-xs text-slate-800 dark:text-slate-100">
|
||||
<span class="text-xs font-medium text-slate-800 dark:text-slate-100">
|
||||
{{ $t('INBOX.DISPLAY_MENU.SORT') }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -64,7 +64,7 @@
|
||||
</div>
|
||||
<div>
|
||||
<span
|
||||
class="font-medium text-xs py-4 px-3 text-slate-400 dark:text-slate-400"
|
||||
class="px-3 py-4 text-xs font-medium text-slate-400 dark:text-slate-400"
|
||||
>
|
||||
{{ $t('INBOX.DISPLAY_MENU.DISPLAY') }}
|
||||
</span>
|
||||
@@ -98,10 +98,17 @@
|
||||
|
||||
<script>
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
export default {
|
||||
mixins: [uiSettingsMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showSortMenu: false,
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="p-4 border-slate-25 dark:border-slate-700 text-black-900 dark:text-slate-300 flex flex-row"
|
||||
class="flex flex-row p-4 border-slate-25 dark:border-slate-700 text-black-900 dark:text-slate-300"
|
||||
>
|
||||
<div
|
||||
class="flex-grow-0 flex-shrink-0 flex-[25%] min-w-0 py-4 pr-6 pl-0"
|
||||
@@ -103,7 +103,7 @@
|
||||
<woot-code :script="getAccountId" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-sm text-center p-4">
|
||||
<div class="p-4 text-sm text-center">
|
||||
<div>{{ `v${globalConfig.appVersion}` }}</div>
|
||||
<div v-if="hasAnUpdateAvailable && globalConfig.displayManifest">
|
||||
{{
|
||||
@@ -132,15 +132,22 @@
|
||||
import { required, minValue, maxValue } from 'vuelidate/lib/validators';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import configMixin from 'shared/mixins/configMixin';
|
||||
import accountMixin from '../../../../mixins/account';
|
||||
import { FEATURE_FLAGS } from '../../../../featureFlags';
|
||||
import semver from 'semver';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
|
||||
export default {
|
||||
mixins: [accountMixin, configMixin, uiSettingsMixin],
|
||||
mixins: [accountMixin, configMixin],
|
||||
setup() {
|
||||
const { updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: '',
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import configMixin from 'shared/mixins/configMixin';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import AudioAlertTone from './AudioAlertTone.vue';
|
||||
import AudioAlertEvent from './AudioAlertEvent.vue';
|
||||
import AudioAlertCondition from './AudioAlertCondition.vue';
|
||||
@@ -43,7 +43,15 @@ export default {
|
||||
AudioAlertTone,
|
||||
AudioAlertCondition,
|
||||
},
|
||||
mixins: [configMixin, uiSettingsMixin],
|
||||
mixins: [configMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
audioAlert: '',
|
||||
@@ -56,7 +64,6 @@ export default {
|
||||
computed: {
|
||||
...mapGetters({
|
||||
accountId: 'getCurrentAccountId',
|
||||
uiSettings: 'getUISettings',
|
||||
}),
|
||||
},
|
||||
watch: {
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
:description="hotKey.description"
|
||||
:light-image="hotKey.lightImage"
|
||||
:dark-image="hotKey.darkImage"
|
||||
:active="isEditorHotKeyEnabled(uiSettings, hotKey.key)"
|
||||
:active="isEditorHotKeyEnabled(hotKey.key)"
|
||||
@click="toggleHotKey(hotKey.key)"
|
||||
/>
|
||||
</button>
|
||||
@@ -81,15 +81,12 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
import uiSettingsMixin, {
|
||||
isEditorHotKeyEnabled,
|
||||
} from 'dashboard/mixins/uiSettings';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { clearCookiesOnLogout } from 'dashboard/store/utils/api.js';
|
||||
import { copyTextToClipboard } from 'shared/helpers/clipboard';
|
||||
|
||||
import globalConfigMixin from 'shared/mixins/globalConfigMixin';
|
||||
import UserProfilePicture from './UserProfilePicture.vue';
|
||||
import UserBasicDetails from './UserBasicDetails.vue';
|
||||
import MessageSignature from './MessageSignature.vue';
|
||||
@@ -112,7 +109,17 @@ export default {
|
||||
AudioNotifications,
|
||||
AccessToken,
|
||||
},
|
||||
mixins: [globalConfigMixin, uiSettingsMixin],
|
||||
mixins: [globalConfigMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings, isEditorHotKeyEnabled } =
|
||||
useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
isEditorHotKeyEnabled,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
avatarFile: '',
|
||||
@@ -168,7 +175,6 @@ export default {
|
||||
this.displayName = this.currentUser.display_name;
|
||||
this.messageSignature = this.currentUser.message_signature;
|
||||
},
|
||||
isEditorHotKeyEnabled,
|
||||
async dispatchUpdate(payload, successMessage, errorMessage) {
|
||||
let alertMessage = '';
|
||||
try {
|
||||
|
||||
@@ -134,7 +134,6 @@
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import configMixin from 'shared/mixins/configMixin';
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import TableHeaderCell from 'dashboard/components/widgets/TableHeaderCell.vue';
|
||||
import CheckBox from 'v3/components/Form/CheckBox.vue';
|
||||
import {
|
||||
@@ -152,7 +151,7 @@ export default {
|
||||
FormSwitch,
|
||||
CheckBox,
|
||||
},
|
||||
mixins: [configMixin, uiSettingsMixin],
|
||||
mixins: [configMixin],
|
||||
data() {
|
||||
return {
|
||||
selectedEmailFlags: [],
|
||||
@@ -167,7 +166,6 @@ export default {
|
||||
accountId: 'getCurrentAccountId',
|
||||
emailFlags: 'userNotificationSettings/getSelectedEmailFlags',
|
||||
pushFlags: 'userNotificationSettings/getSelectedPushFlags',
|
||||
uiSettings: 'getUISettings',
|
||||
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
|
||||
}),
|
||||
hasPushAPISupport() {
|
||||
@@ -242,31 +240,6 @@ export default {
|
||||
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_ERROR'));
|
||||
}
|
||||
},
|
||||
|
||||
handleAudioInput(e) {
|
||||
this.enableAudioAlerts = e.target.value;
|
||||
this.updateUISettings({
|
||||
enable_audio_alerts: this.enableAudioAlerts,
|
||||
});
|
||||
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
|
||||
},
|
||||
handleAudioAlertConditions(e) {
|
||||
let condition = e.target.value;
|
||||
if (condition === 'tab_is_inactive') {
|
||||
this.updateUISettings({
|
||||
always_play_audio_alert: !e.target.checked,
|
||||
});
|
||||
} else if (condition === 'conversations_are_read') {
|
||||
this.updateUISettings({
|
||||
alert_if_unread_assigned_conversation_exist: e.target.checked,
|
||||
});
|
||||
}
|
||||
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
|
||||
},
|
||||
handleAudioToneChange(value) {
|
||||
this.updateUISettings({ notification_tone: value });
|
||||
useAlert(this.$t('PROFILE_SETTINGS.FORM.API.UPDATE_SUCCESS'));
|
||||
},
|
||||
handleInput(type, id) {
|
||||
if (type === 'email') {
|
||||
this.handleEmailInput(id);
|
||||
|
||||
@@ -42,7 +42,7 @@ export default {
|
||||
type: Number,
|
||||
default: 2,
|
||||
},
|
||||
// add this as a prop, so that we won't have to include uiSettingsMixin
|
||||
// add this as a prop, so that we won't have to add useUISettings
|
||||
sendWithSignature: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import uiSettingsMixin from 'dashboard/mixins/uiSettings';
|
||||
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
export default {
|
||||
mixins: [uiSettingsMixin],
|
||||
setup() {
|
||||
const { uiSettings, updateUISettings } = useUISettings();
|
||||
|
||||
return {
|
||||
uiSettings,
|
||||
updateUISettings,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isRTLView() {
|
||||
const { rtl_view: isRTLView } = this.uiSettings;
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
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],
|
||||
computed: {
|
||||
uiSettings() {
|
||||
return { rtl_view };
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user