mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-01 03:27:52 +00:00
feat: Rewrite configMixin to a composable (#9921)
# Pull Request Template ## Description This PR will replace the usage of `configMixin` with the `useConfig` composable. **Files updated** 1. dashboard/components/layout/sidebarComponents/SecondaryNavItem.vue 2. dashboard/components/widgets/conversation/MessagesView.vue 3. dashboard/routes/dashboard/settings/inbox/Settings.vue **(Not used)** 4. dashboard/routes/dashboard/settings/inbox/FinishSetup.vue **(Not used)** 5. dashboard/routes/dashboard/settings/inbox/settingsPage/CollaboratorsPage.vue 6. dashboard/routes/dashboard/settings/profile/NotificationPreferences.vue **(Not used)** 7. dashboard/routes/dashboard/settings/profile/AudioNotifications.vue **(Not used)** 8. dashboard/routes/dashboard/settings/sla/Index.vue **(Not used)** 9. dashboard/routes/dashboard/settings/account/Index.vue 10. survey/views/Response.vue **(Not used)** Fixes https://linear.app/chatwoot/issue/CW-3464/rewrite-configmixin-mixin-to-a-composable ## Type of change - [x] New feature (non-breaking change which adds functionality) ## How Has This Been Tested? Test in the component related pages ## Checklist: - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my code - [x] I have commented on my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [x] My changes generate no new warnings - [x] I have added tests that prove my fix is effective or that my feature works - [x] New and existing unit tests pass locally with my changes - [ ] Any dependent changes have been merged and published in downstream modules
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { useAdmin } from 'dashboard/composables/useAdmin';
|
import { useAdmin } from 'dashboard/composables/useAdmin';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
import { useConfig } from 'dashboard/composables/useConfig';
|
||||||
import {
|
import {
|
||||||
getInboxClassByType,
|
getInboxClassByType,
|
||||||
getInboxWarningIconClass,
|
getInboxWarningIconClass,
|
||||||
@@ -16,7 +16,6 @@ import Policy from '../../policy.vue';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { SecondaryChildNavItem, Policy },
|
components: { SecondaryChildNavItem, Policy },
|
||||||
mixins: [configMixin],
|
|
||||||
props: {
|
props: {
|
||||||
menuItem: {
|
menuItem: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -25,8 +24,10 @@ export default {
|
|||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const { isAdmin } = useAdmin();
|
const { isAdmin } = useAdmin();
|
||||||
|
const { isEnterprise } = useConfig();
|
||||||
return {
|
return {
|
||||||
isAdmin,
|
isAdmin,
|
||||||
|
isEnterprise,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
// composable
|
// composable
|
||||||
|
import { useConfig } from 'dashboard/composables/useConfig';
|
||||||
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents';
|
||||||
|
|
||||||
// components
|
// components
|
||||||
@@ -14,7 +15,6 @@ import { mapGetters } from 'vuex';
|
|||||||
|
|
||||||
// mixins
|
// mixins
|
||||||
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
import aiMixin from 'dashboard/mixins/aiMixin';
|
import aiMixin from 'dashboard/mixins/aiMixin';
|
||||||
|
|
||||||
// utils
|
// utils
|
||||||
@@ -40,7 +40,7 @@ export default {
|
|||||||
Banner,
|
Banner,
|
||||||
ConversationLabelSuggestion,
|
ConversationLabelSuggestion,
|
||||||
},
|
},
|
||||||
mixins: [inboxMixin, configMixin, aiMixin],
|
mixins: [inboxMixin, aiMixin],
|
||||||
props: {
|
props: {
|
||||||
isContactPanelOpen: {
|
isContactPanelOpen: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
@@ -54,6 +54,7 @@ export default {
|
|||||||
setup() {
|
setup() {
|
||||||
const conversationFooterRef = ref(null);
|
const conversationFooterRef = ref(null);
|
||||||
const isPopOutReplyBox = ref(false);
|
const isPopOutReplyBox = ref(false);
|
||||||
|
const { isEnterprise } = useConfig();
|
||||||
|
|
||||||
const closePopOutReplyBox = () => {
|
const closePopOutReplyBox = () => {
|
||||||
isPopOutReplyBox.value = false;
|
isPopOutReplyBox.value = false;
|
||||||
@@ -72,6 +73,7 @@ export default {
|
|||||||
useKeyboardEvents(keyboardEvents, conversationFooterRef);
|
useKeyboardEvents(keyboardEvents, conversationFooterRef);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
isEnterprise,
|
||||||
conversationFooterRef,
|
conversationFooterRef,
|
||||||
isPopOutReplyBox,
|
isPopOutReplyBox,
|
||||||
closePopOutReplyBox,
|
closePopOutReplyBox,
|
||||||
|
|||||||
51
app/javascript/dashboard/composables/spec/useConfig.spec.js
Normal file
51
app/javascript/dashboard/composables/spec/useConfig.spec.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { useConfig } from '../useConfig';
|
||||||
|
|
||||||
|
describe('useConfig', () => {
|
||||||
|
const originalChatwootConfig = window.chatwootConfig;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
window.chatwootConfig = {
|
||||||
|
hostURL: 'https://example.com',
|
||||||
|
vapidPublicKey: 'vapid-key',
|
||||||
|
enabledLanguages: ['en', 'fr'],
|
||||||
|
isEnterprise: 'true',
|
||||||
|
enterprisePlanName: 'enterprise',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
window.chatwootConfig = originalChatwootConfig;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns the correct configuration values', () => {
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
|
expect(config.hostURL).toBe('https://example.com');
|
||||||
|
expect(config.vapidPublicKey).toBe('vapid-key');
|
||||||
|
expect(config.enabledLanguages).toEqual(['en', 'fr']);
|
||||||
|
expect(config.isEnterprise).toBe(true);
|
||||||
|
expect(config.enterprisePlanName).toBe('enterprise');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles missing configuration values', () => {
|
||||||
|
window.chatwootConfig = {};
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
|
expect(config.hostURL).toBeUndefined();
|
||||||
|
expect(config.vapidPublicKey).toBeUndefined();
|
||||||
|
expect(config.enabledLanguages).toBeUndefined();
|
||||||
|
expect(config.isEnterprise).toBe(false);
|
||||||
|
expect(config.enterprisePlanName).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('handles undefined window.chatwootConfig', () => {
|
||||||
|
window.chatwootConfig = undefined;
|
||||||
|
const config = useConfig();
|
||||||
|
|
||||||
|
expect(config.hostURL).toBeUndefined();
|
||||||
|
expect(config.vapidPublicKey).toBeUndefined();
|
||||||
|
expect(config.enabledLanguages).toBeUndefined();
|
||||||
|
expect(config.isEnterprise).toBe(false);
|
||||||
|
expect(config.enterprisePlanName).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
46
app/javascript/dashboard/composables/useConfig.js
Normal file
46
app/javascript/dashboard/composables/useConfig.js
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* A function that provides access to various configuration values.
|
||||||
|
* @returns {Object} An object containing configuration values.
|
||||||
|
*/
|
||||||
|
export function useConfig() {
|
||||||
|
const config = window.chatwootConfig || {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The host URL of the Chatwoot instance.
|
||||||
|
* @type {string|undefined}
|
||||||
|
*/
|
||||||
|
const hostURL = config.hostURL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The VAPID public key for web push notifications.
|
||||||
|
* @type {string|undefined}
|
||||||
|
*/
|
||||||
|
const vapidPublicKey = config.vapidPublicKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An array of enabled languages in the Chatwoot instance.
|
||||||
|
* @type {string[]|undefined}
|
||||||
|
*/
|
||||||
|
const enabledLanguages = config.enabledLanguages;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the current instance is an enterprise version.
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
const isEnterprise = config.isEnterprise === 'true';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the enterprise plan, if applicable.
|
||||||
|
* Returns "community" or "enterprise"
|
||||||
|
* @type {string|undefined}
|
||||||
|
*/
|
||||||
|
const enterprisePlanName = config.enterprisePlanName;
|
||||||
|
|
||||||
|
return {
|
||||||
|
hostURL,
|
||||||
|
vapidPublicKey,
|
||||||
|
enabledLanguages,
|
||||||
|
isEnterprise,
|
||||||
|
enterprisePlanName,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -4,18 +4,18 @@ import { required, minValue, maxValue } from '@vuelidate/validators';
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
import { useConfig } from 'dashboard/composables/useConfig';
|
||||||
import { FEATURE_FLAGS } from '../../../../featureFlags';
|
import { FEATURE_FLAGS } from '../../../../featureFlags';
|
||||||
import semver from 'semver';
|
import semver from 'semver';
|
||||||
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [configMixin],
|
|
||||||
setup() {
|
setup() {
|
||||||
const { updateUISettings } = useUISettings();
|
const { updateUISettings } = useUISettings();
|
||||||
|
const { enabledLanguages } = useConfig();
|
||||||
const v$ = useVuelidate();
|
const v$ = useVuelidate();
|
||||||
|
|
||||||
return { updateUISettings, v$ };
|
return { updateUISettings, v$, enabledLanguages };
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,12 +1,10 @@
|
|||||||
<script>
|
<script>
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
import EmptyState from '../../../../components/widgets/EmptyState.vue';
|
import EmptyState from '../../../../components/widgets/EmptyState.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
EmptyState,
|
EmptyState,
|
||||||
},
|
},
|
||||||
mixins: [configMixin],
|
|
||||||
computed: {
|
computed: {
|
||||||
currentInbox() {
|
currentInbox() {
|
||||||
return this.$store.getters['inboxes/getInbox'](
|
return this.$store.getters['inboxes/getInbox'](
|
||||||
@@ -94,11 +92,11 @@ export default {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="isWhatsAppCloudInbox" class="w-[50%] max-w-[50%] ml-[25%]">
|
<div v-if="isWhatsAppCloudInbox" class="w-[50%] max-w-[50%] ml-[25%]">
|
||||||
<p class="text-slate-700 dark:text-slate-200 font-medium mt-8">
|
<p class="mt-8 font-medium text-slate-700 dark:text-slate-200">
|
||||||
{{ $t('INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_URL') }}
|
{{ $t('INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_URL') }}
|
||||||
</p>
|
</p>
|
||||||
<woot-code lang="html" :script="currentInbox.callback_webhook_url" />
|
<woot-code lang="html" :script="currentInbox.callback_webhook_url" />
|
||||||
<p class="text-slate-700 dark:text-slate-200 font-medium mt-8">
|
<p class="mt-8 font-medium text-slate-700 dark:text-slate-200">
|
||||||
{{
|
{{
|
||||||
$t(
|
$t(
|
||||||
'INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_VERIFICATION_TOKEN'
|
'INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_VERIFICATION_TOKEN'
|
||||||
@@ -132,7 +130,7 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
<div class="flex justify-center gap-2 mt-4">
|
<div class="flex justify-center gap-2 mt-4">
|
||||||
<router-link
|
<router-link
|
||||||
class="button hollow primary rounded"
|
class="rounded button hollow primary"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'settings_inbox_show',
|
name: 'settings_inbox_show',
|
||||||
params: { inboxId: $route.params.inbox_id },
|
params: { inboxId: $route.params.inbox_id },
|
||||||
@@ -141,7 +139,7 @@ export default {
|
|||||||
{{ $t('INBOX_MGMT.FINISH.MORE_SETTINGS') }}
|
{{ $t('INBOX_MGMT.FINISH.MORE_SETTINGS') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
<router-link
|
<router-link
|
||||||
class="button success rounded"
|
class="rounded button success"
|
||||||
:to="{
|
:to="{
|
||||||
name: 'inbox_dashboard',
|
name: 'inbox_dashboard',
|
||||||
params: { inboxId: $route.params.inbox_id },
|
params: { inboxId: $route.params.inbox_id },
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { shouldBeUrl } from 'shared/helpers/Validators';
|
import { shouldBeUrl } from 'shared/helpers/Validators';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import { useVuelidate } from '@vuelidate/core';
|
import { useVuelidate } from '@vuelidate/core';
|
||||||
import SettingIntroBanner from 'dashboard/components/widgets/SettingIntroBanner.vue';
|
import SettingIntroBanner from 'dashboard/components/widgets/SettingIntroBanner.vue';
|
||||||
@@ -34,7 +33,7 @@ export default {
|
|||||||
SenderNameExamplePreview,
|
SenderNameExamplePreview,
|
||||||
MicrosoftReauthorize,
|
MicrosoftReauthorize,
|
||||||
},
|
},
|
||||||
mixins: [configMixin, inboxMixin],
|
mixins: [inboxMixin],
|
||||||
setup() {
|
setup() {
|
||||||
return { v$: useVuelidate() };
|
return { v$: useVuelidate() };
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,14 +3,13 @@ import { mapGetters } from 'vuex';
|
|||||||
import { useVuelidate } from '@vuelidate/core';
|
import { useVuelidate } from '@vuelidate/core';
|
||||||
import { minValue } from '@vuelidate/validators';
|
import { minValue } from '@vuelidate/validators';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
import { useConfig } from 'dashboard/composables/useConfig';
|
||||||
import SettingsSection from '../../../../../components/SettingsSection.vue';
|
import SettingsSection from '../../../../../components/SettingsSection.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SettingsSection,
|
SettingsSection,
|
||||||
},
|
},
|
||||||
mixins: [configMixin],
|
|
||||||
props: {
|
props: {
|
||||||
inbox: {
|
inbox: {
|
||||||
type: Object,
|
type: Object,
|
||||||
@@ -18,7 +17,9 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
return { v$: useVuelidate() };
|
const { isEnterprise } = useConfig();
|
||||||
|
|
||||||
|
return { v$: useVuelidate(), isEnterprise };
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<script>
|
<script>
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||||
import AudioAlertTone from './AudioAlertTone.vue';
|
import AudioAlertTone from './AudioAlertTone.vue';
|
||||||
import AudioAlertEvent from './AudioAlertEvent.vue';
|
import AudioAlertEvent from './AudioAlertEvent.vue';
|
||||||
@@ -12,7 +11,6 @@ export default {
|
|||||||
AudioAlertTone,
|
AudioAlertTone,
|
||||||
AudioAlertCondition,
|
AudioAlertCondition,
|
||||||
},
|
},
|
||||||
mixins: [configMixin],
|
|
||||||
setup() {
|
setup() {
|
||||||
const { uiSettings, updateUISettings } = useUISettings();
|
const { uiSettings, updateUISettings } = useUISettings();
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
import TableHeaderCell from 'dashboard/components/widgets/TableHeaderCell.vue';
|
import TableHeaderCell from 'dashboard/components/widgets/TableHeaderCell.vue';
|
||||||
import CheckBox from 'v3/components/Form/CheckBox.vue';
|
import CheckBox from 'v3/components/Form/CheckBox.vue';
|
||||||
import {
|
import {
|
||||||
@@ -19,7 +18,6 @@ export default {
|
|||||||
FormSwitch,
|
FormSwitch,
|
||||||
CheckBox,
|
CheckBox,
|
||||||
},
|
},
|
||||||
mixins: [configMixin],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedEmailFlags: [],
|
selectedEmailFlags: [],
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import SLAPaywallEnterprise from './components/SLAPaywallEnterprise.vue';
|
|||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { convertSecondsToTimeUnit } from '@chatwoot/utils';
|
import { convertSecondsToTimeUnit } from '@chatwoot/utils';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -22,7 +21,6 @@ export default {
|
|||||||
SLAListItemLoading,
|
SLAListItemLoading,
|
||||||
SLAPaywallEnterprise,
|
SLAPaywallEnterprise,
|
||||||
},
|
},
|
||||||
mixins: [configMixin],
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: {},
|
loading: {},
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
export default {
|
|
||||||
computed: {
|
|
||||||
hostURL() {
|
|
||||||
return window.chatwootConfig.hostURL;
|
|
||||||
},
|
|
||||||
vapidPublicKey() {
|
|
||||||
return window.chatwootConfig.vapidPublicKey;
|
|
||||||
},
|
|
||||||
enabledLanguages() {
|
|
||||||
return window.chatwootConfig.enabledLanguages;
|
|
||||||
},
|
|
||||||
isEnterprise() {
|
|
||||||
return window.chatwootConfig.isEnterprise === 'true';
|
|
||||||
},
|
|
||||||
enterprisePlanName() {
|
|
||||||
// returns "community" or "enterprise"
|
|
||||||
return window.chatwootConfig?.enterprisePlanName;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -5,7 +5,6 @@ import Spinner from 'shared/components/Spinner.vue';
|
|||||||
import Rating from 'survey/components/Rating.vue';
|
import Rating from 'survey/components/Rating.vue';
|
||||||
import Feedback from 'survey/components/Feedback.vue';
|
import Feedback from 'survey/components/Feedback.vue';
|
||||||
import Banner from 'survey/components/Banner.vue';
|
import Banner from 'survey/components/Banner.vue';
|
||||||
import configMixin from 'shared/mixins/configMixin';
|
|
||||||
import { getSurveyDetails, updateSurvey } from 'survey/api/survey';
|
import { getSurveyDetails, updateSurvey } from 'survey/api/survey';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -17,8 +16,6 @@ export default {
|
|||||||
Banner,
|
Banner,
|
||||||
Feedback,
|
Feedback,
|
||||||
},
|
},
|
||||||
mixins: [configMixin],
|
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
surveyDetails: null,
|
surveyDetails: null,
|
||||||
|
|||||||
Reference in New Issue
Block a user