mirror of
https://github.com/lingble/chatwoot.git
synced 2025-11-08 23:13:21 +00:00
# Pull Request Template
## Description
This PR allows users to dynamically pass custom welcome and availability
messages, along with UI feature toggles, via `window.chatwootSettings`.
If any of the following settings are provided, the widget will use them;
otherwise, it falls back to default behavior.
**New options:**
```
window.chatwootSettings = {
welcomeTitle: 'Need help?', // Custom widget title
welcomeDescription: 'We’re here to support you.', // Subtitle in the header
availableMessage: 'We’re online and ready to chat!', // Shown when team is online
unavailableMessage: 'We’re currently offline.', // Shown when team is unavailable
enableFileUpload: true, // Enable file attachments
enableEmojiPicker: true, // Enable emoji picker in chat input
enableEndConversation: true // Allow users to end the conversation
}
```
Fixes
https://linear.app/chatwoot/issue/CW-4589/add-options-to-windowchatwootsettings
## Type of change
- [x] New feature (non-breaking change which adds functionality)
## How Has This Been Tested?
### Loom video
https://www.loom.com/share/413fc4aa59384366b071450bd19d1bf8?sid=ff30fb4c-267c-4beb-80ab-d6f583aa960d
## Checklist:
- [x] My code follows the style guidelines of this project
- [x] I have performed a self-review of my code
- [ ] 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
- [ ] 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
---------
Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com>
106 lines
3.0 KiB
Vue
106 lines
3.0 KiB
Vue
<script>
|
|
import { mapGetters } from 'vuex';
|
|
import { getContrastingTextColor } from '@chatwoot/utils';
|
|
import nextAvailabilityTime from 'widget/mixins/nextAvailabilityTime';
|
|
import configMixin from 'widget/mixins/configMixin';
|
|
import availabilityMixin from 'widget/mixins/availability';
|
|
import { IFrameHelper } from 'widget/helpers/utils';
|
|
import { CHATWOOT_ON_START_CONVERSATION } from '../constants/sdkEvents';
|
|
import GroupedAvatars from 'widget/components/GroupedAvatars.vue';
|
|
|
|
export default {
|
|
name: 'TeamAvailability',
|
|
components: {
|
|
GroupedAvatars,
|
|
},
|
|
mixins: [configMixin, nextAvailabilityTime, availabilityMixin],
|
|
props: {
|
|
availableAgents: {
|
|
type: Array,
|
|
default: () => {},
|
|
},
|
|
hasConversation: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
},
|
|
emits: ['startConversation'],
|
|
|
|
computed: {
|
|
...mapGetters({
|
|
widgetColor: 'appConfig/getWidgetColor',
|
|
availableMessage: 'appConfig/getAvailableMessage',
|
|
unavailableMessage: 'appConfig/getUnavailableMessage',
|
|
}),
|
|
textColor() {
|
|
return getContrastingTextColor(this.widgetColor);
|
|
},
|
|
agentAvatars() {
|
|
return this.availableAgents.map(agent => ({
|
|
name: agent.name,
|
|
avatar: agent.avatar_url,
|
|
id: agent.id,
|
|
}));
|
|
},
|
|
headerMessage() {
|
|
return this.isOnline
|
|
? this.availableMessage || this.$t('TEAM_AVAILABILITY.ONLINE')
|
|
: this.unavailableMessage || this.$t('TEAM_AVAILABILITY.OFFLINE');
|
|
},
|
|
isOnline() {
|
|
const { workingHoursEnabled } = this.channelConfig;
|
|
const anyAgentOnline = this.availableAgents.length > 0;
|
|
|
|
if (workingHoursEnabled) {
|
|
return this.isInBetweenTheWorkingHours;
|
|
}
|
|
return anyAgentOnline;
|
|
},
|
|
},
|
|
methods: {
|
|
startConversation() {
|
|
this.$emit('startConversation');
|
|
if (!this.hasConversation) {
|
|
IFrameHelper.sendMessage({
|
|
event: 'onEvent',
|
|
eventIdentifier: CHATWOOT_ON_START_CONVERSATION,
|
|
data: { hasConversation: false },
|
|
});
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
class="flex flex-col gap-3 w-full shadow outline-1 outline outline-n-container rounded-xl bg-n-background dark:bg-n-solid-2 px-5 py-4"
|
|
>
|
|
<div class="flex items-center justify-between gap-2">
|
|
<div class="flex flex-col gap-1">
|
|
<div class="font-medium text-n-slate-12 line-clamp-2">
|
|
{{ headerMessage }}
|
|
</div>
|
|
<div class="text-n-slate-11">
|
|
{{ replyWaitMessage }}
|
|
</div>
|
|
</div>
|
|
<GroupedAvatars v-if="isOnline" :users="availableAgents" />
|
|
</div>
|
|
<button
|
|
class="inline-flex items-center gap-1 font-medium text-n-slate-12"
|
|
:style="{ color: widgetColor }"
|
|
@click="startConversation"
|
|
>
|
|
<span>
|
|
{{
|
|
hasConversation
|
|
? $t('CONTINUE_CONVERSATION')
|
|
: $t('START_CONVERSATION')
|
|
}}
|
|
</span>
|
|
<i class="i-lucide-chevron-right size-5 mt-px" />
|
|
</button>
|
|
</div>
|
|
</template>
|