mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 19:17:48 +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> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import { useAdmin } from 'dashboard/composables/useAdmin'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import { useConfig } from 'dashboard/composables/useConfig'; | ||||
| import { | ||||
|   getInboxClassByType, | ||||
|   getInboxWarningIconClass, | ||||
| @@ -16,7 +16,6 @@ import Policy from '../../policy.vue'; | ||||
|  | ||||
| export default { | ||||
|   components: { SecondaryChildNavItem, Policy }, | ||||
|   mixins: [configMixin], | ||||
|   props: { | ||||
|     menuItem: { | ||||
|       type: Object, | ||||
| @@ -25,8 +24,10 @@ export default { | ||||
|   }, | ||||
|   setup() { | ||||
|     const { isAdmin } = useAdmin(); | ||||
|     const { isEnterprise } = useConfig(); | ||||
|     return { | ||||
|       isAdmin, | ||||
|       isEnterprise, | ||||
|     }; | ||||
|   }, | ||||
|   computed: { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| <script> | ||||
| import { ref } from 'vue'; | ||||
| // composable | ||||
| import { useConfig } from 'dashboard/composables/useConfig'; | ||||
| import { useKeyboardEvents } from 'dashboard/composables/useKeyboardEvents'; | ||||
|  | ||||
| // components | ||||
| @@ -14,7 +15,6 @@ import { mapGetters } from 'vuex'; | ||||
|  | ||||
| // mixins | ||||
| import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import aiMixin from 'dashboard/mixins/aiMixin'; | ||||
|  | ||||
| // utils | ||||
| @@ -40,7 +40,7 @@ export default { | ||||
|     Banner, | ||||
|     ConversationLabelSuggestion, | ||||
|   }, | ||||
|   mixins: [inboxMixin, configMixin, aiMixin], | ||||
|   mixins: [inboxMixin, aiMixin], | ||||
|   props: { | ||||
|     isContactPanelOpen: { | ||||
|       type: Boolean, | ||||
| @@ -54,6 +54,7 @@ export default { | ||||
|   setup() { | ||||
|     const conversationFooterRef = ref(null); | ||||
|     const isPopOutReplyBox = ref(false); | ||||
|     const { isEnterprise } = useConfig(); | ||||
|  | ||||
|     const closePopOutReplyBox = () => { | ||||
|       isPopOutReplyBox.value = false; | ||||
| @@ -72,6 +73,7 @@ export default { | ||||
|     useKeyboardEvents(keyboardEvents, conversationFooterRef); | ||||
|  | ||||
|     return { | ||||
|       isEnterprise, | ||||
|       conversationFooterRef, | ||||
|       isPopOutReplyBox, | ||||
|       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 { useAlert } from 'dashboard/composables'; | ||||
| import { useUISettings } from 'dashboard/composables/useUISettings'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import { useConfig } from 'dashboard/composables/useConfig'; | ||||
| import { FEATURE_FLAGS } from '../../../../featureFlags'; | ||||
| import semver from 'semver'; | ||||
| import { getLanguageDirection } from 'dashboard/components/widgets/conversation/advancedFilterItems/languages'; | ||||
|  | ||||
| export default { | ||||
|   mixins: [configMixin], | ||||
|   setup() { | ||||
|     const { updateUISettings } = useUISettings(); | ||||
|     const { enabledLanguages } = useConfig(); | ||||
|     const v$ = useVuelidate(); | ||||
|  | ||||
|     return { updateUISettings, v$ }; | ||||
|     return { updateUISettings, v$, enabledLanguages }; | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|   | ||||
| @@ -1,12 +1,10 @@ | ||||
| <script> | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import EmptyState from '../../../../components/widgets/EmptyState.vue'; | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
|     EmptyState, | ||||
|   }, | ||||
|   mixins: [configMixin], | ||||
|   computed: { | ||||
|     currentInbox() { | ||||
|       return this.$store.getters['inboxes/getInbox']( | ||||
| @@ -94,11 +92,11 @@ export default { | ||||
|           /> | ||||
|         </div> | ||||
|         <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') }} | ||||
|           </p> | ||||
|           <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( | ||||
|                 'INBOX_MGMT.ADD.WHATSAPP.API_CALLBACK.WEBHOOK_VERIFICATION_TOKEN' | ||||
| @@ -132,7 +130,7 @@ export default { | ||||
|         </div> | ||||
|         <div class="flex justify-center gap-2 mt-4"> | ||||
|           <router-link | ||||
|             class="button hollow primary rounded" | ||||
|             class="rounded button hollow primary" | ||||
|             :to="{ | ||||
|               name: 'settings_inbox_show', | ||||
|               params: { inboxId: $route.params.inbox_id }, | ||||
| @@ -141,7 +139,7 @@ export default { | ||||
|             {{ $t('INBOX_MGMT.FINISH.MORE_SETTINGS') }} | ||||
|           </router-link> | ||||
|           <router-link | ||||
|             class="button success rounded" | ||||
|             class="rounded button success" | ||||
|             :to="{ | ||||
|               name: 'inbox_dashboard', | ||||
|               params: { inboxId: $route.params.inbox_id }, | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| <script> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import { shouldBeUrl } from 'shared/helpers/Validators'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import { useAlert } from 'dashboard/composables'; | ||||
| import { useVuelidate } from '@vuelidate/core'; | ||||
| import SettingIntroBanner from 'dashboard/components/widgets/SettingIntroBanner.vue'; | ||||
| @@ -34,7 +33,7 @@ export default { | ||||
|     SenderNameExamplePreview, | ||||
|     MicrosoftReauthorize, | ||||
|   }, | ||||
|   mixins: [configMixin, inboxMixin], | ||||
|   mixins: [inboxMixin], | ||||
|   setup() { | ||||
|     return { v$: useVuelidate() }; | ||||
|   }, | ||||
|   | ||||
| @@ -3,14 +3,13 @@ import { mapGetters } from 'vuex'; | ||||
| import { useVuelidate } from '@vuelidate/core'; | ||||
| import { minValue } from '@vuelidate/validators'; | ||||
| import { useAlert } from 'dashboard/composables'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import { useConfig } from 'dashboard/composables/useConfig'; | ||||
| import SettingsSection from '../../../../../components/SettingsSection.vue'; | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
|     SettingsSection, | ||||
|   }, | ||||
|   mixins: [configMixin], | ||||
|   props: { | ||||
|     inbox: { | ||||
|       type: Object, | ||||
| @@ -18,7 +17,9 @@ export default { | ||||
|     }, | ||||
|   }, | ||||
|   setup() { | ||||
|     return { v$: useVuelidate() }; | ||||
|     const { isEnterprise } = useConfig(); | ||||
|  | ||||
|     return { v$: useVuelidate(), isEnterprise }; | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| <script> | ||||
| import { useAlert } from 'dashboard/composables'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import { useUISettings } from 'dashboard/composables/useUISettings'; | ||||
| import AudioAlertTone from './AudioAlertTone.vue'; | ||||
| import AudioAlertEvent from './AudioAlertEvent.vue'; | ||||
| @@ -12,7 +11,6 @@ export default { | ||||
|     AudioAlertTone, | ||||
|     AudioAlertCondition, | ||||
|   }, | ||||
|   mixins: [configMixin], | ||||
|   setup() { | ||||
|     const { uiSettings, updateUISettings } = useUISettings(); | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,6 @@ | ||||
| <script> | ||||
| import { mapGetters } from 'vuex'; | ||||
| import { useAlert } from 'dashboard/composables'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import TableHeaderCell from 'dashboard/components/widgets/TableHeaderCell.vue'; | ||||
| import CheckBox from 'v3/components/Form/CheckBox.vue'; | ||||
| import { | ||||
| @@ -19,7 +18,6 @@ export default { | ||||
|     FormSwitch, | ||||
|     CheckBox, | ||||
|   }, | ||||
|   mixins: [configMixin], | ||||
|   data() { | ||||
|     return { | ||||
|       selectedEmailFlags: [], | ||||
|   | ||||
| @@ -10,7 +10,6 @@ import SLAPaywallEnterprise from './components/SLAPaywallEnterprise.vue'; | ||||
| import { mapGetters } from 'vuex'; | ||||
| import { convertSecondsToTimeUnit } from '@chatwoot/utils'; | ||||
| import { useAlert } from 'dashboard/composables'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
|  | ||||
| export default { | ||||
|   components: { | ||||
| @@ -22,7 +21,6 @@ export default { | ||||
|     SLAListItemLoading, | ||||
|     SLAPaywallEnterprise, | ||||
|   }, | ||||
|   mixins: [configMixin], | ||||
|   data() { | ||||
|     return { | ||||
|       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 Feedback from 'survey/components/Feedback.vue'; | ||||
| import Banner from 'survey/components/Banner.vue'; | ||||
| import configMixin from 'shared/mixins/configMixin'; | ||||
| import { getSurveyDetails, updateSurvey } from 'survey/api/survey'; | ||||
|  | ||||
| export default { | ||||
| @@ -17,8 +16,6 @@ export default { | ||||
|     Banner, | ||||
|     Feedback, | ||||
|   }, | ||||
|   mixins: [configMixin], | ||||
|  | ||||
|   data() { | ||||
|     return { | ||||
|       surveyDetails: null, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Sivin Varghese
					Sivin Varghese