Compare commits

...
15 Commits
Author SHA1 Message Date
Shivam Mishra ce276f888a fix: remove error loader 2024-10-04 18:09:28 +05:30
Vishnu NarayananandGitHub 8c54d7f794 feat: support vite build for linux installations(cwctl) (#10231)
- Switch to `pnpm` from `yarn`

Changelog
----
- add support for `pnpm` `vite` build for chatwoot 4.0
- for new installations, install pnpm and vite
- for existing installations, remove `node_modules` and install `pnpm`

Note: `yarn` is not removed when upgrading existing installations. If
you want to rollback to an older version of Chatwoot(pre 4.0),

```
sudo -i -u chatwoot
cd chatwoot
git checkout <tag> # tag is the version of chatwoot you want to rollback to
rm -rf node_modules # remove deps installed via pnpm

# Update dependencies
bundle
yarn

# Recompile the assets
rake assets:precompile RAILS_ENV=production

# Migrate the database schema
RAILS_ENV=production bundle exec rake db:migrate
exit
```
2024-10-04 16:50:44 +05:30
Shivam MishraandGitHub bd88bfb0fe fix: emits for FormSelect and PhoneInput component (#10226) 2024-10-04 15:09:42 +05:30
Vishnu NarayananandGitHub 83100b8f60 feat: support vite build for docker based installations (#10225)
- Switch to `pnpm` from `yarn` for docker builds
2024-10-04 14:20:56 +05:30
PranavandGitHub 0f26351fd3 fix: Update card labels button with a new one to fix the CSS issue (#10222) 2024-10-04 00:00:35 -07:00
Sojan JoseandGitHub 6d053e5816 chore: Move line attachment creation to a single db commit (#10194)
Similar to the work we did with the legacy code of other channels like Twilio, we need to move the message creation and attachment creation to be under a single db commit for the line channel. Otherwise, the emitted webhook events for message creation will miss the attachment payload.


Ref: https://github.com/orgs/chatwoot/discussions/7546#discussioncomment-10814495
Ref: https://github.com/chatwoot/chatwoot/pull/10167
2024-10-03 22:13:41 -07:00
Shivam MishraandGitHub bbb8e57fee fix: Update the styles for the datepicker in custom snooze modal (#10207) 2024-10-03 22:06:51 -07:00
Shivam MishraandGitHub 5eac95732b fix: Update translation payload for audit logs (#10217)
Vue i18n would automatically merge arrays previously, it does not do so now. This PR fixes it by cleaning up the payload before passing it for translation
2024-10-03 22:04:26 -07:00
b3262597c1 fix: vue 3 followup fixes (#10213)
Fixes: CW-3602,  CW-3606, CW-3605, CW-3601, CW-3603, CW-3600, CW-3598

-
[CW-3602](https://linear.app/chatwoot/issue/CW-3602/chat-list-infinite-loader-fetching-only-odd-numbered-pages)
Chat list pagination broken
-
[CW-3606](https://linear.app/chatwoot/issue/CW-3606/saving-greeting-message-is-not-working-in-inbox-settings)
Greetings message not getting saved
-
[CW-3605](https://linear.app/chatwoot/issue/CW-3605/copy-and-paste-image-attachment-not-working-in-widget)
Paste not working on widget
-
[CW-3601](https://linear.app/chatwoot/issue/CW-3601/edit-category-is-not-working-properly)
Edit category not updating
-
[CW-3603](https://linear.app/chatwoot/issue/CW-3603/delete-filter-is-not-working)
Delete filter modal not toggling
-
[CW-3600](https://linear.app/chatwoot/issue/CW-3600/portal-editor-is-not-working-properly)
Portal editor events were flaky
-
[CW-3598](https://linear.app/chatwoot/issue/CW-3598/rearrange-of-pre-chat-form-fields-throws-an-error)
Prechat form re-order bug

---------

Co-authored-by: Vishnu Narayanan <iamwishnu@gmail.com>
2024-10-03 19:59:07 +05:30
Shivam MishraandGitHub 701135df92 fix: vitest resolution in vite.config [CW-3587] (#10204)
The ruby plugin conflicted with vitest resolution, this PR fixes it. We
will need to separate out the vite config for this
2024-10-03 19:54:57 +05:30
Vishnu NarayananandGitHub fdc3e370ef fix: gh actions for vite build (#10212)
Fixes https://linear.app/chatwoot/issue/CW-3604/fix-gh-actions-for-vite-build

- [x] response bot spec
- [x] chatwoot ce spec
- [x] size limit check
2024-10-03 16:25:48 +05:30
Shivam MishraandGitHub f14edd5242 fix: initOnEvents not removed [CW-3594] (#10200)
The `initOnEvents` was used to get the notification sound file and
trigger the 30 second loop, but since the function was replaced to using
class syntax, the removeEvent listener was not working. This PR fixes it
by reverting to the old syntax but moving it inside the constructor
instead and also adding a `once: true` to ensure it is always removed
automatically
2024-10-03 15:03:44 +05:30
Shivam MishraandGitHub 578dce81a1 fix: suggestions not triggering directly (#10211) 2024-10-03 15:02:56 +05:30
Shivam MishraandGitHub c51a458c25 style: apply fixes for eslint issues [cw-3590] (#10210)
These fixes are all auto generated and can be merged directly

Fixes the following issues

1. Event used on components should be hypenated
2. Attribute orders in components
3. Use `unmounted` instead of `destroyed`
4. Add explicit `emits` declarations for components, autofixed [using
this
script](https://gist.github.com/scmmishra/6f549109b96400006bb69bbde392eddf)


We ignore the top level v-if for now, we will fix it later
2024-10-03 15:02:12 +05:30
Shivam MishraandGitHub b8d0252511 feat: remove usage of .sync and define explicitly emits (#10209)
References

- https://v3-migration.vuejs.org/breaking-changes/v-model
-
https://v3-migration.vuejs.org/breaking-changes/v-on-native-modifier-removed.html
2024-10-03 12:44:18 +05:30
313 changed files with 1464 additions and 644 deletions
+7 -3
View File
@@ -49,13 +49,17 @@ jobs:
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: pnpm/action-setup@v2
with:
version: 9.3.0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: yarn
cache: pnpm
- name: yarn
run: yarn install
- name: pnpm
run: pnpm install
- name: Create database
run: bundle exec rake db:create
+15 -5
View File
@@ -19,23 +19,33 @@ jobs:
with:
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
- uses: pnpm/action-setup@v2
with:
version: 9.3.0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'yarn'
cache: 'pnpm'
- name: yarn
run: yarn install
- name: pnpm
run: pnpm install
- name: Strip enterprise code
run: |
rm -rf enterprise
rm -rf spec/enterprise
- name: setup env
run: |
cp .env.example .env
- name: Run asset compile
run: bundle exec rake assets:precompile
env:
NODE_OPTIONS: --openssl-legacy-provider
RAILS_ENV: production
- name: Size Check
run: yarn run size
run: pnpm run size
+1 -1
View File
@@ -1 +1 @@
3.9.0
3.13.0
+1 -1
View File
@@ -1 +1 @@
2.8.0
3.0.0
@@ -537,12 +537,6 @@ function loadMoreConversations() {
return;
}
// Increment the current page
store.dispatch('conversationPage/setCurrentPage', {
filter: currentPageFilterKey.value,
page: currentFiltersPage.value + 1,
});
if (!hasAppliedFiltersOrActiveFolders.value) {
fetchConversations();
} else if (hasActiveFolders.value) {
@@ -769,11 +763,11 @@ watch(conversationFilters, (newVal, oldVal) => {
:has-applied-filters="hasAppliedFilters"
:has-active-folders="hasActiveFolders"
:active-status="activeStatus"
@addFolders="onClickOpenAddFoldersModal"
@deleteFolders="onClickOpenDeleteFoldersModal"
@filtersModal="onToggleAdvanceFiltersModal"
@resetFilters="resetAndFetchData"
@basicFilterChange="onBasicFilterChange"
@add-folders="onClickOpenAddFoldersModal"
@delete-folders="onClickOpenDeleteFoldersModal"
@filters-modal="onToggleAdvanceFiltersModal"
@reset-filters="resetAndFetchData"
@basic-filter-change="onBasicFilterChange"
/>
<AddCustomViews
@@ -785,7 +779,7 @@ watch(conversationFilters, (newVal, oldVal) => {
<DeleteCustomViews
v-if="showDeleteFoldersModal"
:show-delete-popup.sync="showDeleteFoldersModal"
v-model:show="showDeleteFoldersModal"
:active-custom-view="activeFolder"
:custom-views-id="foldersId"
:open-last-item-after-delete="openLastItemAfterDeleteInFolder"
@@ -796,7 +790,7 @@ watch(conversationFilters, (newVal, oldVal) => {
v-if="!hasAppliedFiltersOrActiveFolders"
:items="assigneeTabItems"
:active-tab="activeAssigneeTab"
@chatTabChange="updateAssigneeTab"
@chat-tab-change="updateAssigneeTab"
/>
<p
@@ -813,11 +807,11 @@ watch(conversationFilters, (newVal, oldVal) => {
:show-open-action="allSelectedConversationsStatus('open')"
:show-resolved-action="allSelectedConversationsStatus('resolved')"
:show-snoozed-action="allSelectedConversationsStatus('snoozed')"
@selectAllConversations="toggleSelectAll"
@assignAgent="onAssignAgent"
@updateConversations="onUpdateConversations"
@assignLabels="onAssignLabels"
@assignTeam="onAssignTeamsForBulk"
@select-all-conversations="toggleSelectAll"
@assign-agent="onAssignAgent"
@update-conversations="onUpdateConversations"
@assign-labels="onAssignLabels"
@assign-team="onAssignTeamsForBulk"
/>
<div
ref="conversationListRef"
@@ -854,8 +848,8 @@ watch(conversationFilters, (newVal, oldVal) => {
:folders-id="foldersId"
:conversation-type="conversationType"
:show-assignee="showAssigneeInConversationCard"
@selectConversation="selectConversation"
@deSelectConversation="deSelectConversation"
@select-conversation="selectConversation"
@de-select-conversation="deSelectConversation"
/>
</DynamicScrollerItem>
</template>
@@ -878,7 +872,7 @@ watch(conversationFilters, (newVal, oldVal) => {
</DynamicScroller>
</div>
<woot-modal
:show.sync="showAdvancedFilters"
v-model:show="showAdvancedFilters"
:on-close="closeAdvanceFiltersModal"
size="medium"
>
@@ -889,8 +883,8 @@ watch(conversationFilters, (newVal, oldVal) => {
:active-folder-name="activeFolderName"
:on-close="closeAdvanceFiltersModal"
:is-folder-view="hasActiveFolders"
@applyFilter="onApplyFilter"
@updateFolder="onUpdateSavedFilter"
@apply-filter="onApplyFilter"
@update-folder="onUpdateSavedFilter"
/>
</woot-modal>
</div>
@@ -108,7 +108,7 @@ const hasAppliedFiltersOrActiveFolders = computed(() => {
/>
<ConversationBasicFilter
v-if="!hasAppliedFiltersOrActiveFolders"
@changeFilter="onBasicFilterChange"
@change-filter="onBasicFilterChange"
/>
</div>
</div>
@@ -56,14 +56,14 @@ export default {
:selected="isConversationSelected(source.id)"
:show-assignee="showAssignee"
enable-context-menu
@selectConversation="selectConversation"
@deSelectConversation="deSelectConversation"
@assignAgent="assignAgent"
@assignTeam="assignTeam"
@assignLabel="assignLabels"
@updateConversationStatus="updateConversationStatus"
@contextMenuToggle="toggleContextMenu"
@markAsUnread="markAsUnread"
@assignPriority="assignPriority"
@select-conversation="selectConversation"
@de-select-conversation="deSelectConversation"
@assign-agent="assignAgent"
@assign-team="assignTeam"
@assign-label="assignLabels"
@update-conversation-status="updateConversationStatus"
@context-menu-toggle="toggleContextMenu"
@mark-as-unread="markAsUnread"
@assign-priority="assignPriority"
/>
</template>
@@ -31,6 +31,7 @@ export default {
attributeKey: { type: String, required: true },
contactId: { type: Number, default: null },
},
emits: ['update', 'delete', 'copy'],
setup() {
return { v$: useVuelidate() };
},
@@ -138,7 +139,7 @@ export default {
this.editedValue = this.formattedValue;
emitter.on(BUS_EVENTS.FOCUS_CUSTOM_ATTRIBUTE, this.onFocusAttribute);
},
destroyed() {
unmounted() {
emitter.off(BUS_EVENTS.FOCUS_CUSTOM_ATTRIBUTE, this.onFocusAttribute);
},
methods: {
@@ -18,6 +18,7 @@ const shouldShowContent = computed(
);
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<div v-if="shouldShowContent">
<slot />
@@ -5,6 +5,7 @@ export default {
components: {
DatePicker,
},
emits: ['close', 'chooseTime'],
data() {
return {
@@ -43,15 +44,18 @@ export default {
<template>
<div class="flex flex-col">
<woot-modal-header :header-title="$t('CONVERSATION.CUSTOM_SNOOZE.TITLE')" />
<form class="modal-content" @submit.prevent="chooseTime">
<form
class="modal-content w-full pt-2 px-5 pb-6"
@submit.prevent="chooseTime"
>
<DatePicker
v-model:value="snoozeTime"
type="datetime"
inline
input-class="mx-input reset-base"
:lang="lang"
:disabled-date="disabledDate"
:disabled-time="disabledTime"
:popup-style="{ width: '100%' }"
/>
<div class="flex flex-row justify-end w-full gap-2 px-0 py-2">
<woot-button variant="clear" @click.prevent="onClose">
@@ -64,9 +68,3 @@ export default {
</form>
</div>
</template>
<style lang="scss" scoped>
.modal-content {
@apply pt-2 px-5 pb-6;
}
</style>
@@ -3,9 +3,8 @@
import { ref, computed, defineEmits, onMounted } from 'vue';
import { useEventListener } from '@vueuse/core';
const { show, modalType, closeOnBackdropClick, onClose } = defineProps({
const { modalType, closeOnBackdropClick, onClose } = defineProps({
closeOnBackdropClick: { type: Boolean, default: true },
show: Boolean,
showCloseButton: { type: Boolean, default: true },
onClose: { type: Function, required: true },
fullWidth: { type: Boolean, default: false },
@@ -14,6 +13,7 @@ const { show, modalType, closeOnBackdropClick, onClose } = defineProps({
});
const emit = defineEmits(['close']);
const show = defineModel('show', { type: Boolean, default: false });
const modalClassName = computed(() => {
const modalClassNameMap = {
@@ -32,6 +32,7 @@ const handleMouseDown = () => {
};
const close = () => {
show.value = false;
emit('close');
onClose();
};
@@ -40,14 +41,14 @@ const onMouseUp = () => {
if (mousedDownOnBackdrop.value) {
mousedDownOnBackdrop.value = false;
if (closeOnBackdropClick) {
onClose();
close();
}
}
};
const onKeydown = e => {
if (show && e.code === 'Escape') {
onClose();
if (show.value && e.code === 'Escape') {
close();
e.stopPropagation();
}
};
@@ -79,6 +79,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<Banner
v-if="shouldShowBanner"
@@ -86,6 +87,6 @@ export default {
:banner-message="bannerMessage"
:action-button-label="actionButtonMessage"
has-action-button
@primaryAction="routeToBilling"
@primary-action="routeToBilling"
/>
</template>
@@ -28,6 +28,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<Banner
v-if="shouldShowBanner"
@@ -36,6 +37,6 @@ export default {
:action-button-label="actionButtonMessage"
action-button-icon="mail"
has-action-button
@primaryAction="resendVerificationEmail"
@primary-action="resendVerificationEmail"
/>
</template>
@@ -67,6 +67,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<Banner
v-if="shouldShowBanner"
@@ -81,6 +81,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<Banner
v-if="shouldShowBanner"
@@ -88,6 +89,6 @@ export default {
:banner-message="bannerMessage"
:action-button-label="actionButtonMessage"
has-action-button
@primaryAction="routeToBilling"
@primary-action="routeToBilling"
/>
</template>
@@ -22,11 +22,14 @@ export default {
type: Boolean,
default: true,
},
hasBanner: {
type: Boolean,
default: false,
},
},
emits: [
'toggleAccountModal',
'showAddLabelPopup',
'openNotificationPanel',
'closeKeyShortcutModal',
'openKeyShortcutModal',
],
setup(props, { emit }) {
const route = useRoute();
const router = useRouter();
@@ -217,9 +220,9 @@ export default {
:account-id="accountId"
:menu-items="primaryMenuItems"
:active-menu-item="activePrimaryMenu.key"
@toggleAccounts="toggleAccountModal"
@openKeyShortcutModal="toggleKeyShortcutModal"
@openNotificationPanel="openNotificationPanel"
@toggle-accounts="toggleAccountModal"
@open-key-shortcut-modal="toggleKeyShortcutModal"
@open-notification-panel="openNotificationPanel"
/>
<SecondarySidebar
v-if="hasSecondarySidebar"
@@ -231,8 +234,8 @@ export default {
:menu-config="activeSecondaryMenu"
:current-user="currentUser"
:is-on-chatwoot-cloud="isOnChatwootCloud"
@addLabel="showAddLabelPopup"
@toggleAccounts="toggleAccountModal"
@add-label="showAddLabelPopup"
@toggle-accounts="toggleAccountModal"
/>
</aside>
</template>
@@ -2,6 +2,7 @@
import { mapGetters } from 'vuex';
export default {
emits: ['toggleAccounts'],
data() {
return { showSwitchButton: false };
},
@@ -25,6 +26,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<div
v-if="showShowCurrentAccountContext"
@@ -7,7 +7,7 @@ export default {
default: true,
},
},
emits: ['closeAccountModal', 'showCreateAccountModal'],
computed: {
...mapGetters({
accountId: 'getCurrentAccountId',
@@ -15,6 +15,7 @@ export default {
default: true,
},
},
emits: ['closeAccountCreateModal'],
setup() {
return { v$: useVuelidate() };
},
@@ -6,6 +6,7 @@ export default {
components: {
Thumbnail,
},
emits: ['toggleMenu'],
computed: {
...mapGetters({
currentUser: 'getCurrentUser',
@@ -2,6 +2,8 @@
import { mapGetters } from 'vuex';
export default {
emits: ['openNotificationPanel'],
computed: {
...mapGetters({
notificationMetadata: 'notifications/getMeta',
@@ -17,6 +17,12 @@ export default {
default: false,
},
},
emits: [
'close',
'openKeyShortcutModal',
'toggleAccounts',
'showSupportChatWindow',
],
computed: {
...mapGetters({
currentUser: 'getCurrentUser',
@@ -43,6 +43,7 @@ export default {
default: '',
},
},
emits: ['toggleAccounts', 'openNotificationPanel', 'openKeyShortcutModal'],
data() {
return {
helpDocsURL: wootConstants.DOCS_URL,
@@ -96,13 +97,13 @@ export default {
open-in-new-page
:to="helpDocsURL"
/>
<NotificationBell @openNotificationPanel="openNotificationPanel" />
<AgentDetails @toggleMenu="toggleOptions" />
<NotificationBell @open-notification-panel="openNotificationPanel" />
<AgentDetails @toggle-menu="toggleOptions" />
<OptionsMenu
:show="showOptionsMenu"
@toggleAccounts="toggleAccountModal"
@showSupportChatWindow="toggleSupportChatWindow"
@openKeyShortcutModal="$emit('openKeyShortcutModal')"
@toggle-accounts="toggleAccountModal"
@show-support-chat-window="toggleSupportChatWindow"
@open-key-shortcut-modal="$emit('openKeyShortcutModal')"
@close="toggleOptions"
/>
</div>
@@ -49,6 +49,7 @@ export default {
default: false,
},
},
emits: ['addLabel', 'toggleAccounts'],
computed: {
...mapGetters({
isFeatureEnabledonAccount: 'accounts/isFeatureEnabledonAccount',
@@ -242,7 +243,7 @@ export default {
<div
class="flex flex-col w-48 h-full px-2 pb-8 overflow-auto text-sm bg-white border-r dark:bg-slate-900 dark:border-slate-800/50 rtl:border-r-0 rtl:border-l border-slate-50"
>
<AccountContext @toggleAccounts="toggleAccountModal" />
<AccountContext @toggle-accounts="toggleAccountModal" />
<transition-group
name="menu-list"
tag="ul"
@@ -257,7 +258,7 @@ export default {
v-for="menuItem in additionalSecondaryMenuItems[menuConfig.parentNav]"
:key="menuItem.key"
:menu-item="menuItem"
@addLabel="showAddLabelPopup"
@add-label="showAddLabelPopup"
/>
</transition-group>
</div>
@@ -106,10 +106,11 @@ export default {
<span
v-if="showChildCount"
class="bg-slate-50 dark:bg-slate-700 rounded-full min-w-[18px] justify-center items-center flex text-xxs mx-1 py-0 px-1"
:class="isCountZero
:class="
isCountZero
? `text-slate-300 dark:text-slate-500`
: `text-slate-700 dark:text-slate-50`
"
"
>
{{ childItemCount }}
</span>
@@ -22,6 +22,7 @@ export default {
default: () => ({}),
},
},
emits: ['addLabel', 'open'],
setup() {
const { isAdmin } = useAdmin();
const { isEnterprise } = useConfig();
@@ -18,6 +18,7 @@ export default {
default: '',
},
},
emits: ['open', 'close'],
methods: {
onClickOpenPath() {
this.$emit('open');
@@ -38,6 +38,7 @@ export default {
default: false,
},
},
emits: ['primaryAction', 'close'],
computed: {
bannerClasses() {
const classList = [this.colorScheme];
@@ -222,7 +222,7 @@ const emitDateRange = () => {
>
<CalendarDateRange
:selected-range="selectedRange"
@setRange="setDateRange"
@set-range="setDateRange"
/>
<div
class="flex flex-col w-[680px] ltr:border-l rtl:border-r border-slate-50 dark:border-slate-700/50"
@@ -264,15 +264,15 @@ const emitDateRange = () => {
:calendar-type="calendar"
:start-current-date="startCurrentDate"
:end-current-date="endCurrentDate"
@selectYear="openCalendar($event, calendar, YEAR)"
@select-year="openCalendar($event, calendar, YEAR)"
/>
<CalendarMonth
v-else-if="calendarViews[calendar] === MONTH"
:calendar-type="calendar"
:start-current-date="startCurrentDate"
:end-current-date="endCurrentDate"
@selectMonth="openCalendar($event, calendar)"
@setView="setViewMode"
@select-month="openCalendar($event, calendar)"
@set-view="setViewMode"
@prev="moveCalendar(calendar, 'prev', YEAR)"
@next="moveCalendar(calendar, 'next', YEAR)"
/>
@@ -286,9 +286,9 @@ const emitDateRange = () => {
:selected-end-date="selectedEndDate"
:selecting-end-date="selectingEndDate"
:hovered-end-date="hoveredEndDate"
@updateHoveredEndDate="hoveredEndDate = $event"
@selectDate="selectDate"
@setView="setViewMode"
@update-hovered-end-date="hoveredEndDate = $event"
@select-date="selectDate"
@set-view="setViewMode"
@prev="moveCalendar(calendar, 'prev')"
@next="moveCalendar(calendar, 'next')"
/>
@@ -1,5 +1,5 @@
<script setup>
const emit = defineEmits(['clear', 'clear']);
const emit = defineEmits(['clear', 'change']);
const onClickClear = () => {
emit('clear');
@@ -62,7 +62,7 @@ const selectMonth = index => {
MONTH
)
"
@setView="setViewMode"
@set-view="setViewMode"
@prev="onClickPrev"
@next="onClickNext"
/>
@@ -139,7 +139,7 @@ const dayClasses = day => ({
"
@prev="onClickPrev"
@next="onClickNext"
@setView="setViewMode"
@set-view="setViewMode"
/>
<CalendarWeekLabel />
<div
@@ -16,6 +16,7 @@ export default {
default: () => [],
},
},
emits: ['change'],
methods: {
handleChange(value) {
this.$emit('change', value);
@@ -17,6 +17,7 @@ export default {
default: [],
},
},
emits: ['change'],
methods: {
handleChange(value) {
@@ -38,7 +38,7 @@ const props = defineProps({
},
});
const emit = defineEmits(['onSearch', 'select']);
const emit = defineEmits(['onSearch', 'select', 'removeFilter']);
const searchTerm = ref('');
@@ -44,6 +44,7 @@ export default {
default: '',
},
},
emits: ['remove'],
computed: {
textColor() {
if (this.variant === 'smooth') return '';
@@ -4,6 +4,7 @@ export default {
modelValue: { type: Boolean, default: false },
size: { type: String, default: '' },
},
emits: ['update:modelValue', 'input'],
methods: {
onClick() {
this.$emit('update:modelValue', !this.modelValue);
@@ -17,6 +17,7 @@ export default {
AICTAModal,
AIAssistanceCTAButton,
},
emits: ['replaceText'],
setup(props, { emit }) {
const { uiSettings, updateUISettings } = useUISettings();
@@ -137,19 +138,19 @@ export default {
@click="openAIAssist"
/>
<woot-modal
:show.sync="showAIAssistanceModal"
v-model:show="showAIAssistanceModal"
:on-close="hideAIAssistanceModal"
>
<AIAssistanceModal
:ai-option="aiOption"
@applyText="insertText"
@apply-text="insertText"
@close="hideAIAssistanceModal"
/>
</woot-modal>
</div>
<div v-else-if="shouldShowAIAssistCTAButtonForAdmin" class="relative">
<AIAssistanceCTAButton @click="openAICta" />
<woot-modal :show.sync="showAICtaModal" :on-close="hideAICtaModal">
<woot-modal v-model:show="showAICtaModal" :on-close="hideAICtaModal">
<AICTAModal @close="hideAICtaModal" />
</woot-modal>
</div>
@@ -1,5 +1,7 @@
<script>
export default {
emits: ['open'],
methods: {
onClick() {
this.$emit('open');
@@ -13,6 +13,7 @@ export default {
required: true,
},
},
emits: ['close', 'applyText'],
setup() {
const { formatMessage } = useMessageFormatter();
const { draftMessage, processEvent, recordAnalytics } = useAI();
@@ -7,6 +7,8 @@ import { useAI } from 'dashboard/composables/useAI';
import { OPEN_AI_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
export default {
emits: ['close'],
setup() {
const { updateUISettings } = useUISettings();
const { recordAnalytics } = useAI();
@@ -38,6 +38,7 @@ export default {
default: false,
},
},
emits: ['update:modelValue', 'input', 'removeAction', 'resetAction'],
computed: {
action_name: {
get() {
@@ -3,6 +3,7 @@ export default {
// The value types are dynamic, hence prop validation removed to work with our action schema
// eslint-disable-next-line vue/require-prop-types
props: ['teams', 'value'],
emits: ['input'],
data() {
return {
selectedTeams: [],
@@ -11,6 +11,7 @@ export default {
default: '',
},
},
emits: ['update:modelValue'],
data() {
return {
uploadState: 'idle',
@@ -12,6 +12,7 @@ export default {
required: true,
},
},
emits: ['channelItemClick'],
computed: {
hasFbConfigured() {
return window.chatwootConfig?.fbAppId;
@@ -11,6 +11,7 @@ export default {
default: '',
},
},
emits: ['update:modelValue'],
data() {
return {
isPickerOpen: false,
@@ -45,7 +46,7 @@ export default {
disable-alpha
:model-value="modelValue"
class="colorpicker--chrome"
@update:modelValue="updateColor"
@update:model-value="updateColor"
/>
</div>
</template>
@@ -80,6 +80,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<div v-if="hasOpenedAtleastOnce" class="dashboard-app--container">
<div
@@ -19,6 +19,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<div v-if="isFeatureEnabled">
<slot />
@@ -47,6 +47,7 @@ export default {
default: '',
},
},
emits: ['update:modelValue', 'removeFilter', 'resetFilter'],
computed: {
attributeKey: {
get() {
@@ -5,7 +5,7 @@ export default {
props: {
inbox: {
type: Object,
default: () => { },
default: () => {},
},
},
computed: {
@@ -16,6 +16,8 @@ const props = defineProps({
default: 0,
},
});
const emit = defineEmits(['pageChange']);
const totalPages = computed(() => Math.ceil(props.totalCount / props.pageSize));
const firstIndex = computed(() => props.pageSize * (props.currentPage - 1) + 1);
const lastIndex = computed(() =>
@@ -26,6 +28,7 @@ const isFooterVisible = computed(
);
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<footer
v-if="isFooterVisible"
@@ -42,7 +45,7 @@ const isFooterVisible = computed(
:total-pages="totalPages"
:total-count="totalCount"
:page-size="pageSize"
@pageChange="$emit('pageChange', $event)"
@page-change="emit('pageChange', $event)"
/>
</footer>
</template>
@@ -41,6 +41,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<woot-button
v-if="isVideoIntegrationEnabled"
@@ -176,7 +176,7 @@ const editorMenuOptions = computed(() => {
function createSuggestionPlugin({
trigger,
minChars = 1,
minChars = 0,
showMenu,
searchTerm,
isAllowed = () => true,
@@ -47,6 +47,7 @@ export default {
placeholder: { type: String, default: '' },
enabledMenuOptions: { type: Array, default: () => [] },
},
emits: ['blur', 'input', 'update:modelValue', 'keyup', 'focus', 'keydown'],
setup() {
const { uiSettings, updateUISettings } = useUISettings();
@@ -60,17 +61,9 @@ export default {
plugins: [imagePastePlugin(this.handleImageUpload)],
};
},
computed: {
contentFromEditor() {
if (editorView) {
return ArticleMarkdownSerializer.serialize(editorView.state.doc);
}
return '';
},
},
watch: {
modelValue(newValue = '') {
if (newValue !== this.contentFromEditor) {
if (newValue !== this.contentFromEditor()) {
this.reloadState();
}
},
@@ -95,6 +88,12 @@ export default {
this.focusEditorInputField();
},
methods: {
contentFromEditor() {
if (editorView) {
return ArticleMarkdownSerializer.serialize(editorView.state.doc);
}
return '';
},
openFileBrowser() {
this.$refs.imageUploadInput.click();
},
@@ -212,8 +211,8 @@ export default {
editorView.focus();
},
emitOnChange() {
this.$emit('update:modelValue', this.contentFromEditor);
this.$emit('input', this.contentFromEditor);
this.$emit('update:modelValue', this.contentFromEditor());
this.$emit('input', this.contentFromEditor());
},
onKeyup() {
this.$emit('keyup');
@@ -26,7 +26,7 @@ export default {
},
onSend: {
type: Function,
default: () => { },
default: () => {},
},
sendButtonText: {
type: String,
@@ -53,19 +53,19 @@ export default {
},
onFileUpload: {
type: Function,
default: () => { },
default: () => {},
},
toggleEmojiPicker: {
type: Function,
default: () => { },
default: () => {},
},
toggleAudioRecorder: {
type: Function,
default: () => { },
default: () => {},
},
toggleAudioRecorderPlayPause: {
type: Function,
default: () => { },
default: () => {},
},
isRecordingAudio: {
type: Boolean,
@@ -112,6 +112,12 @@ export default {
required: true,
},
},
emits: [
'replaceText',
'toggleInsertArticle',
'toggleEditor',
'selectWhatsappTemplate',
],
setup() {
const { setSignatureFlagForInbox, fetchSignatureFlagFromUISettings } =
useUISettings();
@@ -166,9 +172,9 @@ export default {
return false;
}
// Disable audio recorder for safari browser as recording is not supported
const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(
navigator.userAgent
);
// const isSafari = /^((?!chrome|android|crios|fxios).)*safari/i.test(
// navigator.userAgent
// );
return (
this.isFeatureEnabledonAccount(
@@ -340,7 +346,7 @@ export default {
:conversation-id="conversationId"
:is-private-note="isOnPrivateNote"
:message="message"
@replaceText="replaceText"
@replace-text="replaceText"
/>
<transition name="modal-fade">
<div
@@ -22,6 +22,7 @@ export default {
default: false,
},
},
emits: ['setReplyMode', 'togglePopout'],
setup(props, { emit }) {
const setReplyMode = mode => {
emit('setReplyMode', mode);
@@ -48,7 +48,7 @@ onMounted(() => {
v-if="items.length"
type="emoji"
:items="items"
@mentionSelect="handleMentionClick"
@mention-select="handleMentionClick"
>
<template #default="{ item, selected }">
<span
@@ -10,6 +10,7 @@ export default {
default: '',
},
},
emits: ['replace'],
computed: {
...mapGetters({
cannedMessages: 'getCannedResponses',
@@ -46,6 +47,6 @@ export default {
<MentionBox
v-if="items.length"
:items="items"
@mentionSelect="handleMentionClick"
@mention-select="handleMentionClick"
/>
</template>
@@ -37,6 +37,7 @@ export default {
default: false,
},
},
emits: ['applyFilter', 'updateFolder'],
setup() {
const { setFilterAttributes } = useFilter({
filteri18nKey: 'FILTER',
@@ -363,8 +364,8 @@ export default {
? $t(`CONTACTS_FILTER.ERRORS.VALUE_REQUIRED`)
: ''
"
@resetFilter="resetFilter(i, appliedFilters[i])"
@removeFilter="removeFilter(i)"
@reset-filter="resetFilter(i, appliedFilters[i])"
@remove-filter="removeFilter(i)"
/>
<div class="mt-4">
<woot-button
@@ -27,6 +27,7 @@ export default {
components: {
FilterItem,
},
emits: ['changeFilter'],
setup() {
const { updateUISettings } = useUISettings();
@@ -107,7 +108,7 @@ export default {
:selected-value="chatStatus"
:items="chatStatusItems"
path-prefix="CHAT_LIST.CHAT_STATUS_FILTER_ITEMS"
@onChangeFilter="onChangeFilter"
@on-change-filter="onChangeFilter"
/>
</div>
<div class="flex items-center justify-between last:mt-4">
@@ -119,7 +120,7 @@ export default {
:selected-value="sortFilter"
:items="chatSortItems"
path-prefix="CHAT_LIST.SORT_ORDER_ITEMS"
@onChangeFilter="onChangeFilter"
@on-change-filter="onChangeFilter"
/>
</div>
</div>
@@ -34,6 +34,7 @@ export default {
default: true,
},
},
emits: ['contactPanelToggle'],
data() {
return { activeIndex: 0 };
},
@@ -103,7 +104,7 @@ export default {
:is-inbox-view="isInboxView"
:is-contact-panel-open="isContactPanelOpen"
:show-back-button="isOnExpandedLayout && !isInboxView"
@contactPanelToggle="onToggleContactPanel"
@contact-panel-toggle="onToggleContactPanel"
/>
<woot-tabs
v-if="dashboardApps.length && currentChat.id"
@@ -128,7 +129,7 @@ export default {
:inbox-id="inboxId"
:is-inbox-view="isInboxView"
:is-contact-panel-open="isContactPanelOpen"
@contactPanelToggle="onToggleContactPanel"
@contact-panel-toggle="onToggleContactPanel"
/>
<EmptyState
v-if="!currentChat.id && !isInboxView"
@@ -69,6 +69,15 @@ export default {
default: false,
},
},
emits: [
'contextMenuToggle',
'assignAgent',
'assignLabel',
'assignTeam',
'markAsUnread',
'assignPriority',
'updateConversationStatus',
],
data() {
return {
hovered: false,
@@ -336,12 +345,12 @@ export default {
:priority="chat.priority"
:chat-id="chat.id"
:has-unread-messages="hasUnread"
@updateConversation="onUpdateConversation"
@assignAgent="onAssignAgent"
@assignLabel="onAssignLabel"
@assignTeam="onAssignTeam"
@markAsUnread="markAsUnread"
@assignPriority="assignPriority"
@update-conversation="onUpdateConversation"
@assign-agent="onAssignAgent"
@assign-label="onAssignLabel"
@assign-team="onAssignTeam"
@mark-as-unread="markAsUnread"
@assign-priority="assignPriority"
/>
</ContextMenu>
</div>
@@ -41,6 +41,7 @@ export default {
default: false,
},
},
emits: ['contactPanelToggle'],
setup(props, { emit }) {
const keyboardEvents = {
'Alt+KeyO': {
@@ -2,6 +2,7 @@
import { useVuelidate } from '@vuelidate/core';
import { required, minLength, email } from '@vuelidate/validators';
import { useAlert } from 'dashboard/composables';
export default {
props: {
show: {
@@ -13,6 +14,7 @@ export default {
default: () => ({}),
},
},
emits: ['cancel', 'update:show'],
setup() {
return { v$: useVuelidate() };
},
@@ -31,6 +33,14 @@ export default {
},
},
computed: {
localShow: {
get() {
return this.show;
},
set(value) {
this.$emit('update:show', value);
},
},
sentToOtherEmailAddress() {
return this.selectedType === 'other_email_address';
},
@@ -80,9 +90,8 @@ export default {
};
</script>
<!-- eslint-disable vue/no-mutating-props -->
<template>
<woot-modal :show.sync="show" :on-close="onCancel">
<woot-modal v-model:show="localShow" :on-close="onCancel">
<div class="flex flex-col h-auto overflow-auto">
<woot-modal-header
:header-title="$t('EMAIL_TRANSCRIPT.TITLE')"
@@ -18,6 +18,7 @@ export default {
required: true,
},
},
emits: ['onChangeFilter'],
data() {
return {
activeValue: this.selectedValue,
@@ -439,6 +439,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<li
v-if="shouldRenderMessage"
@@ -579,7 +580,7 @@ export default {
:message="data"
@open="openContextMenu"
@close="closeContextMenu"
@replyTo="handleReplyTo"
@reply-to="handleReplyTo"
/>
</div>
</li>
@@ -52,6 +52,7 @@ export default {
default: false,
},
},
emits: ['contactPanelToggle'],
setup() {
const isPopOutReplyBox = ref(false);
const { isEnterprise } = useConfig();
@@ -547,9 +548,9 @@ export default {
</div>
</div>
<ReplyBox
v-model:popout-reply-box="isPopOutReplyBox"
:conversation-id="currentChat.id"
:popout-reply-box.sync="isPopOutReplyBox"
@togglePopout="showPopOutReplyBox"
@toggle-popout="showPopOutReplyBox"
/>
</div>
</div>
@@ -28,7 +28,7 @@ export default {
emitter.on(CMD_UNMUTE_CONVERSATION, this.unmute);
emitter.on(CMD_SEND_TRANSCRIPT, this.toggleEmailActionsModal);
},
destroyed() {
unmounted() {
emitter.off(CMD_MUTE_CONVERSATION, this.mute);
emitter.off(CMD_UNMUTE_CONVERSATION, this.unmute);
emitter.off(CMD_SEND_TRANSCRIPT, this.toggleEmailActionsModal);
@@ -29,6 +29,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<span
v-if="priority"
@@ -1,5 +1,6 @@
<script>
import { defineAsyncComponent } from 'vue';
// [TODO] The popout events are needlessly complex and should be simplified
import { defineAsyncComponent, defineModel } from 'vue';
import { mapGetters } from 'vuex';
import { useAlert } from 'dashboard/composables';
import { useUISettings } from 'dashboard/composables/useUISettings';
@@ -65,12 +66,7 @@ export default {
ArticleSearchPopover,
},
mixins: [inboxMixin, fileUploadMixin, keyboardEventListenerMixins],
props: {
popoutReplyBox: {
type: Boolean,
default: false,
},
},
emits: ['update:popoutReplyBox', 'togglePopout'],
setup() {
const {
uiSettings,
@@ -79,8 +75,14 @@ export default {
fetchSignatureFlagFromUISettings,
} = useUISettings();
const popoutReplyBox = defineModel('popoutReplyBox', {
type: Boolean,
default: false,
});
return {
uiSettings,
popoutReplyBox,
updateUISettings,
isEditorHotKeyEnabled,
fetchSignatureFlagFromUISettings,
@@ -463,12 +465,10 @@ export default {
this.onNewConversationModalActive
);
},
destroyed() {
unmounted() {
document.removeEventListener('paste', this.onPaste);
document.removeEventListener('keydown', this.handleKeyEvents);
emitter.off(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE, this.fetchAndSetReplyTo);
},
unmounted() {
emitter.off(
BUS_EVENTS.NEW_CONVERSATION_MODAL,
this.onNewConversationModalActive
@@ -1081,15 +1081,15 @@ export default {
:banner-message="$t('CONVERSATION.NOT_ASSIGNED_TO_YOU')"
has-action-button
:action-button-label="$t('CONVERSATION.ASSIGN_TO_ME')"
@primaryAction="onClickSelfAssign"
@primary-action="onClickSelfAssign"
/>
<ReplyTopPanel
:mode="replyType"
:is-message-length-reaching-threshold="isMessageLengthReachingThreshold"
:characters-remaining="charactersRemaining"
:popout-reply-box="popoutReplyBox"
@setReplyMode="setReplyMode"
@togglePopout="$emit('togglePopout')"
@set-reply-mode="setReplyMode"
@toggle-popout="$emit('togglePopout')"
/>
<ArticleSearchPopover
v-if="showArticleSearchPopover && connectedPortalSlug"
@@ -1126,9 +1126,9 @@ export default {
v-if="showAudioRecorderEditor"
ref="audioRecorderInput"
:audio-record-format="audioRecordFormat"
@recorderProgressChanged="onRecordProgressChanged"
@stateRecorderChanged="onStateRecorderChanged"
@finishRecord="onFinishRecorder"
@recorder-progress-changed="onRecordProgressChanged"
@state-recorder-changed="onStateRecorderChanged"
@finish-record="onFinishRecorder"
/>
<ResizableTextArea
v-else-if="!showRichContentEditor"
@@ -1140,8 +1140,8 @@ export default {
:signature="signatureToApply"
allow-signature
:send-with-signature="sendWithSignature"
@typingOff="onTypingOff"
@typingOn="onTypingOn"
@typing-off="onTypingOff"
@typing-on="onTypingOn"
@focus="onFocus"
@blur="onBlur"
/>
@@ -1159,21 +1159,21 @@ export default {
:signature="signatureToApply"
allow-signature
:channel-type="channelType"
@typingOff="onTypingOff"
@typingOn="onTypingOn"
@typing-off="onTypingOff"
@typing-on="onTypingOn"
@focus="onFocus"
@blur="onBlur"
@toggleUserMention="toggleUserMention"
@toggleCannedMenu="toggleCannedMenu"
@toggleVariablesMenu="toggleVariablesMenu"
@clearSelection="clearEditorSelection"
@toggle-user-mention="toggleUserMention"
@toggle-canned-menu="toggleCannedMenu"
@toggle-variables-menu="toggleVariablesMenu"
@clear-selection="clearEditorSelection"
/>
</div>
<div v-if="hasAttachments" class="attachment-preview-box" @paste="onPaste">
<AttachmentPreview
class="flex-col mt-4"
:attachments="attachedFiles"
@removeAttachment="removeAttachment"
@remove-attachment="removeAttachment"
/>
</div>
<MessageSignatureMissingAlert
@@ -1203,16 +1203,16 @@ export default {
:message="message"
:portal-slug="connectedPortalSlug"
:new-conversation-modal-active="newConversationModalActive"
@selectWhatsappTemplate="openWhatsappTemplateModal"
@toggleEditor="toggleRichContentEditor"
@replaceText="replaceText"
@toggleInsertArticle="toggleInsertArticle"
@select-whatsapp-template="openWhatsappTemplateModal"
@toggle-editor="toggleRichContentEditor"
@replace-text="replaceText"
@toggle-insert-article="toggleInsertArticle"
/>
<WhatsappTemplates
:inbox-id="inbox.id"
:show="showWhatsAppTemplatesModal"
@close="hideWhatsappTemplatesModal"
@onSend="onSendWhatsAppReply"
@on-send="onSendWhatsAppReply"
@cancel="hideWhatsappTemplatesModal"
/>
@@ -17,6 +17,7 @@ export default {
default: '',
},
},
emits: ['update:bccEmails', 'update:ccEmails', 'update:toEmails'],
setup() {
return { v$: useVuelidate() };
},
@@ -9,6 +9,7 @@ export default {
required: true,
},
},
emits: ['dismiss'],
};
</script>
@@ -11,6 +11,7 @@ export default {
default: '',
},
},
emits: ['selectVariable'],
computed: {
...mapGetters({
customAttributes: 'attributes/getAttributes',
@@ -62,7 +63,7 @@ export default {
v-if="items.length"
type="variable"
:items="items"
@mentionSelect="handleVariableClick"
@mention-select="handleVariableClick"
/>
</template>
@@ -7,21 +7,30 @@ export default {
TemplateParser,
},
props: {
show: {
type: Boolean,
default: false,
},
inboxId: {
type: Number,
default: undefined,
},
show: {
type: Boolean,
default: true,
},
},
emits: ['onSend', 'cancel', 'update:show'],
data() {
return {
selectedWaTemplate: null,
};
},
computed: {
localShow: {
get() {
return this.show;
},
set(value) {
this.$emit('update:show', value);
},
},
modalHeaderContent() {
return this.selectedWaTemplate
? this.$t('WHATSAPP_TEMPLATES.MODAL.TEMPLATE_SELECTED_SUBTITLE', {
@@ -47,9 +56,8 @@ export default {
};
</script>
<!-- eslint-disable vue/no-mutating-props -->
<template>
<woot-modal :show.sync="show" :on-close="onClose" size="modal-big">
<woot-modal v-model:show="localShow" :on-close="onClose" size="modal-big">
<woot-modal-header
:header-title="$t('WHATSAPP_TEMPLATES.MODAL.TITLE')"
:header-content="modalHeaderContent"
@@ -58,13 +66,13 @@ export default {
<TemplatesPicker
v-if="!selectedWaTemplate"
:inbox-id="inboxId"
@onSelect="pickTemplate"
@on-select="pickTemplate"
/>
<TemplateParser
v-else
:template="selectedWaTemplate"
@resetTemplate="onResetTemplate"
@sendMessage="onSendMessage"
@reset-template="onResetTemplate"
@send-message="onSendMessage"
/>
</div>
</woot-modal>
@@ -19,6 +19,7 @@ export default {
default: () => ({}),
},
},
emits: ['sendMessage', 'resetTemplate'],
setup(props, { emit }) {
const processVariable = str => {
return str.replace(/{{|}}/g, '');
@@ -9,6 +9,7 @@ export default {
default: undefined,
},
},
emits: ['onSelect'],
data() {
return {
query: '',
@@ -7,6 +7,7 @@ export default {
required: true,
},
},
emits: ['error'],
data() {
return {
show: false,
@@ -26,7 +27,7 @@ export default {
<template>
<div class="image message-text__wrap">
<img :src="url" @click="onClick" @error="$emit('error')" />
<woot-modal full-width :show.sync="show" :on-close="onClose">
<woot-modal v-model:show="show" full-width :on-close="onClose">
<img :src="url" class="modal-image skip-context-menu" />
</woot-modal>
</div>
@@ -21,6 +21,7 @@ export default {
required: true,
},
},
emits: ['error'],
data() {
return {
show: false,
@@ -116,7 +117,7 @@ export default {
</audio>
<GalleryView
v-if="show"
:show.sync="show"
v-model:show="show"
:attachment="attachment"
:all-attachments="filteredCurrentChatAttachments"
@error="onImgError"
@@ -15,6 +15,7 @@ export default {
default: '',
},
},
emits: ['error'],
data() {
return {
hasImgStoryError: false,
@@ -31,6 +31,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<DyteVideoCall
v-if="showDyteIntegration"
@@ -45,6 +45,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<div
v-if="showHead"
@@ -106,7 +106,7 @@ export default {
</button>
<GalleryView
v-if="showGalleryViewer"
:show.sync="showGalleryViewer"
v-model:show="showGalleryViewer"
:attachment="attachment"
:all-attachments="availableAttachments"
@error="onClose"
@@ -10,6 +10,7 @@ export default {
default: '',
},
},
emits: ['close'],
computed: {
translationsAvailable() {
return !!Object.keys(this.translations).length;
@@ -6,6 +6,7 @@ export default {
required: true,
},
},
emits: ['error'],
data() {
return {
show: false,
@@ -30,7 +31,7 @@ export default {
<template>
<div class="video message-text__wrap">
<video ref="videoElement" :src="url" muted playsInline @click="onClick" />
<woot-modal :show.sync="show" :on-close="onClose">
<woot-modal v-model:show="show" :on-close="onClose">
<video
:src="url"
controls
@@ -7,10 +7,6 @@ import { messageTimestamp } from 'shared/helpers/timeHelper';
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
const props = defineProps({
show: {
type: Boolean,
required: true,
},
attachment: {
type: Object,
required: true,
@@ -22,6 +18,7 @@ const props = defineProps({
});
const emit = defineEmits(['close']);
const show = defineModel('show', { type: Boolean, default: false });
const getters = useStoreGetters();
@@ -208,11 +205,10 @@ onMounted(() => {
});
</script>
<!-- eslint-disable vue/no-mutating-props -->
<template>
<woot-modal
v-model:show="show"
full-width
:show.sync="show"
:show-close-button="false"
:on-close="onClose"
>
@@ -103,6 +103,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<div
v-if="hasSlaThreshold"
@@ -37,6 +37,14 @@ export default {
default: null,
},
},
emits: [
'updateConversation',
'assignPriority',
'markAsUnread',
'assignAgent',
'assignTeam',
'assignLabel',
],
data() {
return {
STATUS_TYPE: wootConstants.STATUS_TYPE,
@@ -150,6 +150,7 @@ export default {
};
</script>
<!-- eslint-disable-next-line vue/no-root-v-if -->
<template>
<li
v-if="shouldShowSuggestions"
@@ -18,6 +18,7 @@ export default {
default: 0,
},
},
emits: ['select', 'close'],
data() {
return {
query: '',
@@ -48,6 +48,14 @@ export default {
default: false,
},
},
emits: [
'selectAllConversations',
'assignAgent',
'updateConversations',
'assignLabels',
'assignTeam',
'resolveConversations',
],
data() {
return {
showAgentsList: false,
@@ -72,7 +80,7 @@ export default {
this.onCmdResolveConversation
);
},
destroyed() {
unmounted() {
emitter.off(
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
this.onCmdSnoozeConversation
@@ -231,7 +239,7 @@ export default {
<TeamActions
v-if="showTeamsList"
class="team-actions-box"
@assignTeam="assignTeam"
@assign-team="assignTeam"
@close="showTeamsList = false"
/>
</transition>
@@ -240,12 +248,12 @@ export default {
{{ $t('BULK_ACTION.ALL_CONVERSATIONS_SELECTED_ALERT') }}
</div>
<woot-modal
:show.sync="showCustomTimeSnoozeModal"
v-model:show="showCustomTimeSnoozeModal"
:on-close="hideCustomSnoozeModal"
>
<CustomSnoozeModal
@close="hideCustomSnoozeModal"
@chooseTime="customSnoozeTime"
@choose-time="customSnoozeTime"
/>
</woot-modal>
</div>
@@ -2,6 +2,7 @@
import { mapGetters } from 'vuex';
export default {
emits: ['update', 'close', 'assign'],
data() {
return {
query: '',
@@ -1,6 +1,8 @@
<script>
import { mapGetters } from 'vuex';
export default {
emits: ['assignTeam', 'close'],
data() {
return {
query: '',
@@ -79,35 +79,21 @@ const onShowLabels = e => {
'invisible absolute': !showAllLabels && index > labelPosition,
}"
/>
<woot-button
<button
v-if="showExpandLabelButton"
:title="
showAllLabels
? $t('CONVERSATION.CARD.HIDE_LABELS')
: $t('CONVERSATION.CARD.SHOW_LABELS')
"
class="sticky right-0 flex-shrink-0 mr-6 show-more--button rtl:rotate-180"
color-scheme="secondary"
variant="hollow"
:icon="showAllLabels ? 'chevron-left' : 'chevron-right'"
size="tiny"
class="h-5 py-0 px-1 flex-shrink-0 mr-6 ml-0 rtl:ml-6 rtl:mr-0 rtl:rotate-180 text-slate-700 dark:text-slate-200 hover:border-slate-300 border-slate-100 dark:border-slate-700"
@click="onShowLabels"
/>
>
<fluent-icon
:icon="showAllLabels ? 'chevron-left' : 'chevron-right'"
size="12"
/>
</button>
</div>
</div>
</template>
<style lang="scss" scoped>
.show-more--button {
@apply h-5;
&.secondary:focus {
@apply text-slate-700 dark:text-slate-200 border-slate-300 dark:border-slate-700;
}
}
.labels-wrap {
.secondary {
@apply border border-solid border-slate-100 dark:border-slate-700;
}
}
</style>
@@ -63,7 +63,7 @@ const unlinkIssue = () => {
:identifier="issue.identifier"
:link-id="linkId"
:issue-url="issue.url"
@unlinkIssue="unlinkIssue"
@unlink-issue="unlinkIssue"
/>
<span class="mt-2 text-sm font-medium text-ash-900">
@@ -120,7 +120,7 @@ const linkIssue = async () => {
:loading-placeholder="$t('INTEGRATION_SETTINGS.LINEAR.LINK.LOADING')"
enable-search
class="left-0 flex flex-col w-full overflow-y-auto h-fit !max-h-[160px] md:left-auto md:right-0 top-10"
@onSearch="onSearch"
@on-search="onSearch"
@select="onSelectIssue"
/>
</template>
@@ -119,10 +119,10 @@ onMounted(() => {
:issue="linkedIssue.issue"
:link-id="linkedIssue.id"
class="absolute right-0 top-[40px] invisible group-hover:visible"
@unlinkIssue="unlinkIssue"
@unlink-issue="unlinkIssue"
/>
<woot-modal
:show.sync="shouldShowPopup"
v-model:show="shouldShowPopup"
:on-close="closePopup"
:close-on-backdrop-click="false"
class="!items-start [&>div]:!top-12 [&>div]:sticky"
@@ -18,6 +18,7 @@ export default {
default: false,
},
},
emits: ['onAvatarSelect', 'onAvatarDelete'],
watch: {},
methods: {
handleImageUpload(event) {
@@ -38,7 +38,8 @@ export default {
default: () => {},
},
},
created() {
emits: ['update:modelValue', 'input', 'blur'],
mounted() {
// eslint-disable-next-line
console.warn(
'[DEPRECATED] <WootInput> has be deprecated and will be removed soon. Please use v3/components/Form/Input.vue instead'
@@ -8,7 +8,7 @@ import {
export default {
props: {
value: {
modelValue: {
type: [String, Number],
default: '',
},
@@ -29,6 +29,7 @@ export default {
default: false,
},
},
emits: ['blur', 'setCode', 'update:modelValue'],
data() {
return {
selectedIndex: -1,
@@ -36,7 +37,7 @@ export default {
searchCountry: '',
activeCountryCode: getActiveCountryCode(),
activeDialCode: getActiveDialCode(),
phoneNumber: this.value,
phoneNumber: this.modelValue,
};
},
computed: {
@@ -75,12 +76,12 @@ export default {
},
},
watch: {
value() {
const number = parsePhoneNumber(this.value);
modelValue() {
const number = parsePhoneNumber(this.modelValue);
if (number) {
this.activeCountryCode = number.country;
this.activeDialCode = `+${number.countryCallingCode}`;
this.phoneNumber = this.value.replace(
this.phoneNumber = this.modelValue.replace(
`+${number.countryCallingCode}`,
''
);
@@ -102,7 +103,8 @@ export default {
},
onChange(e) {
this.phoneNumber = e.target.value;
this.$emit('input', e.target.value, this.activeDialCode);
this.$emit('update:modelValue', e.target.value);
this.$emit('setCode', this.activeDialCode);
},
onBlur(e) {
this.$emit('blur', e.target.value);
@@ -8,35 +8,15 @@ export default {
Modal,
},
props: {
show: {
type: Boolean,
default: false,
},
title: {
type: String,
default: '',
},
message: {
type: String,
default: '',
},
confirmText: {
type: String,
default: '',
},
rejectText: {
type: String,
default: '',
},
confirmValue: {
type: String,
default: '',
},
confirmPlaceHolderText: {
type: String,
default: '',
},
show: { type: Boolean, default: false },
title: { type: String, default: '' },
message: { type: String, default: '' },
confirmText: { type: String, default: '' },
rejectText: { type: String, default: '' },
confirmValue: { type: String, default: '' },
confirmPlaceHolderText: { type: String, default: '' },
},
emits: ['onClose', 'onConfirm', 'update:show'],
setup() {
return { v$: useVuelidate() };
},
@@ -53,6 +33,16 @@ export default {
},
},
},
computed: {
localShow: {
get() {
return this.show;
},
set(value) {
this.$emit('update:show', value);
},
},
},
methods: {
closeModal() {
this.value = '';
@@ -65,9 +55,8 @@ export default {
};
</script>
<!-- eslint-disable vue/no-mutating-props -->
<template>
<Modal :show.sync="show" :on-close="closeModal">
<Modal v-model:show="localShow" :on-close="closeModal">
<woot-modal-header :header-title="title" :header-content="message" />
<form @submit.prevent="onConfirm">
<woot-input

Some files were not shown because too many files have changed in this diff Show More