feat: Vite + vue 3 💚 (#10047)
Fixes https://github.com/chatwoot/chatwoot/issues/8436 Fixes https://github.com/chatwoot/chatwoot/issues/9767 Fixes https://github.com/chatwoot/chatwoot/issues/10156 Fixes https://github.com/chatwoot/chatwoot/issues/6031 Fixes https://github.com/chatwoot/chatwoot/issues/5696 Fixes https://github.com/chatwoot/chatwoot/issues/9250 Fixes https://github.com/chatwoot/chatwoot/issues/9762 --------- Co-authored-by: Pranav <pranavrajs@gmail.com> Co-authored-by: Sivin Varghese <64252451+iamsivin@users.noreply.github.com>
This commit is contained in:
co-authored by
Pranav
Sivin Varghese
parent
e0bf2bd9d4
commit
42f6621afb
@@ -35,7 +35,7 @@ export default {
|
||||
this.$store.dispatch('getCannedResponse', { searchKey: this.searchKey });
|
||||
},
|
||||
handleMentionClick(item = {}) {
|
||||
this.$emit('click', item.description);
|
||||
this.$emit('replace', item.description);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
+22
-2
@@ -9,6 +9,7 @@ import { useFilter } from 'shared/composables/useFilter';
|
||||
import * as OPERATORS from 'dashboard/components/widgets/FilterInput/FilterOperatorTypes.js';
|
||||
import { CONVERSATION_EVENTS } from '../../../helper/AnalyticsHelper/events';
|
||||
import { validateConversationOrContactFilters } from 'dashboard/helper/validations.js';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -141,8 +142,27 @@ export default {
|
||||
const type = this.filterTypes.find(filter => filter.attributeKey === key);
|
||||
return type?.filterOperators;
|
||||
},
|
||||
statusFilterItems() {
|
||||
return {
|
||||
open: {
|
||||
TEXT: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.open.TEXT'),
|
||||
},
|
||||
resolved: {
|
||||
TEXT: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.resolved.TEXT'),
|
||||
},
|
||||
pending: {
|
||||
TEXT: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.pending.TEXT'),
|
||||
},
|
||||
snoozed: {
|
||||
TEXT: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.snoozed.TEXT'),
|
||||
},
|
||||
all: {
|
||||
TEXT: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS.all.TEXT'),
|
||||
},
|
||||
};
|
||||
},
|
||||
getDropdownValues(type) {
|
||||
const statusFilters = this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS');
|
||||
const statusFilters = this.statusFilterItems();
|
||||
const allCustomAttributes = this.$store.getters[
|
||||
'attributes/getAttributesByModel'
|
||||
](this.attributeModel);
|
||||
@@ -268,7 +288,7 @@ export default {
|
||||
JSON.parse(JSON.stringify(this.appliedFilters))
|
||||
);
|
||||
this.$emit('applyFilter', this.appliedFilters);
|
||||
this.$track(CONVERSATION_EVENTS.APPLY_FILTER, {
|
||||
useTrack(CONVERSATION_EVENTS.APPLY_FILTER, {
|
||||
applied_filters: this.appliedFilters.map(filter => ({
|
||||
key: filter.attribute_key,
|
||||
operator: filter.filter_operator,
|
||||
|
||||
+21
-2
@@ -4,6 +4,25 @@ import { mapGetters } from 'vuex';
|
||||
import FilterItem from './FilterItem.vue';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
|
||||
const CHAT_STATUS_FILTER_ITEMS = Object.freeze([
|
||||
'open',
|
||||
'resolved',
|
||||
'pending',
|
||||
'snoozed',
|
||||
'all',
|
||||
]);
|
||||
|
||||
const SORT_ORDER_ITEMS = Object.freeze([
|
||||
'last_activity_at_asc',
|
||||
'last_activity_at_desc',
|
||||
'created_at_desc',
|
||||
'created_at_asc',
|
||||
'priority_desc',
|
||||
'priority_asc',
|
||||
'waiting_since_asc',
|
||||
'waiting_since_desc',
|
||||
]);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
FilterItem,
|
||||
@@ -18,8 +37,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
showActionsDropdown: false,
|
||||
chatStatusItems: this.$t('CHAT_LIST.CHAT_STATUS_FILTER_ITEMS'),
|
||||
chatSortItems: this.$t('CHAT_LIST.SORT_ORDER_ITEMS'),
|
||||
chatStatusItems: CHAT_STATUS_FILTER_ITEMS,
|
||||
chatSortItems: SORT_ORDER_ITEMS,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -46,10 +46,12 @@ export default {
|
||||
return [
|
||||
{
|
||||
key: 'messages',
|
||||
index: 0,
|
||||
name: this.$t('CONVERSATION.DASHBOARD_APP_TAB_MESSAGES'),
|
||||
},
|
||||
...this.dashboardApps.map(dashboardApp => ({
|
||||
...this.dashboardApps.map((dashboardApp, index) => ({
|
||||
key: `dashboard-${dashboardApp.id}`,
|
||||
index: index + 1,
|
||||
name: dashboardApp.title,
|
||||
})),
|
||||
];
|
||||
@@ -112,6 +114,7 @@ export default {
|
||||
<woot-tabs-item
|
||||
v-for="tab in dashboardAppTabs"
|
||||
:key="tab.key"
|
||||
:index="tab.index"
|
||||
:name="tab.name"
|
||||
:show-badge="false"
|
||||
/>
|
||||
|
||||
@@ -12,6 +12,7 @@ import TimeAgo from 'dashboard/components/ui/TimeAgo.vue';
|
||||
import CardLabels from './conversationCardComponents/CardLabels.vue';
|
||||
import PriorityMark from './PriorityMark.vue';
|
||||
import SLACardLabel from './components/SLACardLabel.vue';
|
||||
import ContextMenu from 'dashboard/components/ui/ContextMenu.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -23,6 +24,7 @@ export default {
|
||||
MessagePreview,
|
||||
PriorityMark,
|
||||
SLACardLabel,
|
||||
ContextMenu,
|
||||
},
|
||||
mixins: [inboxMixin],
|
||||
props: {
|
||||
@@ -316,17 +318,13 @@ export default {
|
||||
{{ unreadCount > 9 ? '9+' : unreadCount }}
|
||||
</span>
|
||||
</div>
|
||||
<CardLabels
|
||||
:conversation-id="chat.id"
|
||||
:conversation-labels="chat.labels"
|
||||
class="mt-0.5 mx-2 mb-0"
|
||||
>
|
||||
<CardLabels :conversation-labels="chat.labels" class="mt-0.5 mx-2 mb-0">
|
||||
<template v-if="hasSlaPolicyId" #before>
|
||||
<SLACardLabel :chat="chat" class="ltr:mr-1 rtl:ml-1" />
|
||||
</template>
|
||||
</CardLabels>
|
||||
</div>
|
||||
<woot-context-menu
|
||||
<ContextMenu
|
||||
v-if="showContextMenu"
|
||||
:x="contextMenu.x"
|
||||
:y="contextMenu.y"
|
||||
@@ -345,7 +343,7 @@ export default {
|
||||
@markAsUnread="markAsUnread"
|
||||
@assignPriority="assignPriority"
|
||||
/>
|
||||
</woot-context-menu>
|
||||
</ContextMenu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ export default {
|
||||
<div v-if="sentToOtherEmailAddress" class="w-[50%] mt-1">
|
||||
<label :class="{ error: v$.email.$error }">
|
||||
<input
|
||||
v-model.trim="email"
|
||||
v-model="email"
|
||||
type="text"
|
||||
:placeholder="$t('EMAIL_TRANSCRIPT.FORM.EMAIL.PLACEHOLDER')"
|
||||
@input="v$.email.$touch"
|
||||
|
||||
@@ -56,7 +56,7 @@ export default {
|
||||
!this.loadingChatList &&
|
||||
this.isAdmin
|
||||
) {
|
||||
return 'h-full overflow-auto';
|
||||
return 'h-full overflow-auto w-full';
|
||||
}
|
||||
return 'flex-1 min-w-0 px-0 flex flex-col items-center justify-center h-full';
|
||||
},
|
||||
@@ -73,7 +73,7 @@ export default {
|
||||
<!-- No inboxes attached -->
|
||||
<div
|
||||
v-if="!inboxesList.length && !uiFlags.isFetching && !loadingChatList"
|
||||
class="clearfix"
|
||||
class="clearfix mx-auto"
|
||||
>
|
||||
<OnboardingView v-if="isAdmin" />
|
||||
<EmptyStateMessage v-else :message="$t('CONVERSATION.NO_INBOX_AGENT')" />
|
||||
|
||||
+2
-2
@@ -15,12 +15,12 @@ export default {
|
||||
<div class="flex flex-col items-center justify-center h-full">
|
||||
<img
|
||||
class="m-4 w-32 hidden dark:block"
|
||||
src="~dashboard/assets/images/no-chat-dark.svg"
|
||||
src="dashboard/assets/images/no-chat-dark.svg"
|
||||
alt="No Chat dark"
|
||||
/>
|
||||
<img
|
||||
class="m-4 w-32 block dark:hidden"
|
||||
src="~dashboard/assets/images/no-chat.svg"
|
||||
src="dashboard/assets/images/no-chat.svg"
|
||||
alt="No Chat"
|
||||
/>
|
||||
<span
|
||||
|
||||
@@ -6,7 +6,7 @@ export default {
|
||||
required: true,
|
||||
},
|
||||
items: {
|
||||
type: Object,
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
type: {
|
||||
@@ -42,8 +42,8 @@ export default {
|
||||
class="bg-slate-25 dark:bg-slate-700 text-xs h-6 my-0 mx-1 py-0 pr-6 pl-2 w-32 border border-solid border-slate-75 dark:border-slate-600 text-slate-800 dark:text-slate-100"
|
||||
@change="onTabChange()"
|
||||
>
|
||||
<option v-for="(value, status) in items" :key="status" :value="status">
|
||||
{{ $t(`${pathPrefix}.${status}.TEXT`) }}
|
||||
<option v-for="value in items" :key="value" :value="value">
|
||||
{{ $t(`${pathPrefix}.${value}.TEXT`) }}
|
||||
</option>
|
||||
</select>
|
||||
</template>
|
||||
|
||||
@@ -21,7 +21,9 @@ import { ACCOUNT_EVENTS } from 'dashboard/helper/AnalyticsHelper/events';
|
||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
import { getDayDifferenceFromNow } from 'shared/helpers/DateHelper';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import * as Sentry from '@sentry/vue';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -349,11 +351,11 @@ export default {
|
||||
},
|
||||
mounted() {
|
||||
this.hasMediaLoadError = false;
|
||||
this.$emitter.on(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL, this.closeContextMenu);
|
||||
emitter.on(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL, this.closeContextMenu);
|
||||
this.setupHighlightTimer();
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$emitter.off(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL, this.closeContextMenu);
|
||||
unmounted() {
|
||||
emitter.off(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL, this.closeContextMenu);
|
||||
clearTimeout(this.higlightTimeout);
|
||||
},
|
||||
methods: {
|
||||
@@ -366,9 +368,6 @@ export default {
|
||||
if (this.hasAttachments && this.data.attachments.length > 0) {
|
||||
return this.compareMessageFileType(this.data, type);
|
||||
}
|
||||
if (this.storyReply) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
compareMessageFileType(messageData, type) {
|
||||
@@ -406,7 +405,7 @@ export default {
|
||||
|
||||
e.preventDefault();
|
||||
if (e.type === 'contextmenu') {
|
||||
this.$track(ACCOUNT_EVENTS.OPEN_MESSAGE_CONTEXT_MENU);
|
||||
useTrack(ACCOUNT_EVENTS.OPEN_MESSAGE_CONTEXT_MENU);
|
||||
}
|
||||
this.contextMenuPosition = {
|
||||
x: e.pageX || e.clientX,
|
||||
@@ -423,7 +422,7 @@ export default {
|
||||
const { conversation_id: conversationId, id: replyTo } = this.data;
|
||||
|
||||
LocalStorage.updateJsonStore(replyStorageKey, conversationId, replyTo);
|
||||
this.$emitter.emit(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE, this.data);
|
||||
emitter.emit(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE, this.data);
|
||||
},
|
||||
setupHighlightTimer() {
|
||||
if (Number(this.$route.query.messageId) !== Number(this.data.id)) {
|
||||
|
||||
@@ -18,6 +18,7 @@ import { mapGetters } from 'vuex';
|
||||
import inboxMixin, { INBOX_FEATURES } from 'shared/mixins/inboxMixin';
|
||||
|
||||
// utils
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import { getTypingUsersText } from '../../../helper/commons';
|
||||
import { calculateScrollTop } from './helpers/scrollTopCalculationHelper';
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
@@ -251,12 +252,12 @@ export default {
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$emitter.on(BUS_EVENTS.SCROLL_TO_MESSAGE, this.onScrollToMessage);
|
||||
emitter.on(BUS_EVENTS.SCROLL_TO_MESSAGE, this.onScrollToMessage);
|
||||
// when a new message comes in, we refetch the label suggestions
|
||||
this.$emitter.on(BUS_EVENTS.FETCH_LABEL_SUGGESTIONS, this.fetchSuggestions);
|
||||
emitter.on(BUS_EVENTS.FETCH_LABEL_SUGGESTIONS, this.fetchSuggestions);
|
||||
// when a message is sent we set the flag to true this hides the label suggestions,
|
||||
// until the chat is changed and the flag is reset in the watch for currentChat
|
||||
this.$emitter.on(BUS_EVENTS.MESSAGE_SENT, () => {
|
||||
emitter.on(BUS_EVENTS.MESSAGE_SENT, () => {
|
||||
this.messageSentSinceOpened = true;
|
||||
});
|
||||
},
|
||||
@@ -267,7 +268,7 @@ export default {
|
||||
this.fetchSuggestions();
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
unmounted() {
|
||||
this.removeBusListeners();
|
||||
this.removeScrollListener();
|
||||
},
|
||||
@@ -323,7 +324,7 @@ export default {
|
||||
this.$store.dispatch('fetchAllAttachments', this.currentChat.id);
|
||||
},
|
||||
removeBusListeners() {
|
||||
this.$emitter.off(BUS_EVENTS.SCROLL_TO_MESSAGE, this.onScrollToMessage);
|
||||
emitter.off(BUS_EVENTS.SCROLL_TO_MESSAGE, this.onScrollToMessage);
|
||||
},
|
||||
onScrollToMessage({ messageId = '' } = {}) {
|
||||
this.$nextTick(() => {
|
||||
@@ -428,7 +429,7 @@ export default {
|
||||
} else {
|
||||
this.hasUserScrolled = true;
|
||||
}
|
||||
this.$emitter.emit(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL);
|
||||
emitter.emit(BUS_EVENTS.ON_MESSAGE_LIST_SCROLL);
|
||||
this.fetchPreviousMessages(e.target.scrollTop);
|
||||
},
|
||||
|
||||
@@ -540,7 +541,7 @@ export default {
|
||||
{{ typingUserNames }}
|
||||
<img
|
||||
class="w-6 ltr:ml-2 rtl:mr-2"
|
||||
src="~dashboard/assets/images/typing.gif"
|
||||
src="assets/images/typing.gif"
|
||||
alt="Someone is typing"
|
||||
/>
|
||||
</div>
|
||||
@@ -548,7 +549,7 @@ export default {
|
||||
<ReplyBox
|
||||
:conversation-id="currentChat.id"
|
||||
:popout-reply-box.sync="isPopOutReplyBox"
|
||||
@click="showPopOutReplyBox"
|
||||
@togglePopout="showPopOutReplyBox"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import EmailTranscriptModal from './EmailTranscriptModal.vue';
|
||||
import ResolveAction from '../../buttons/ResolveAction.vue';
|
||||
import {
|
||||
@@ -23,14 +24,14 @@ export default {
|
||||
...mapGetters({ currentChat: 'getSelectedChat' }),
|
||||
},
|
||||
mounted() {
|
||||
this.$emitter.on(CMD_MUTE_CONVERSATION, this.mute);
|
||||
this.$emitter.on(CMD_UNMUTE_CONVERSATION, this.unmute);
|
||||
this.$emitter.on(CMD_SEND_TRANSCRIPT, this.toggleEmailActionsModal);
|
||||
emitter.on(CMD_MUTE_CONVERSATION, this.mute);
|
||||
emitter.on(CMD_UNMUTE_CONVERSATION, this.unmute);
|
||||
emitter.on(CMD_SEND_TRANSCRIPT, this.toggleEmailActionsModal);
|
||||
},
|
||||
destroyed() {
|
||||
this.$emitter.off(CMD_MUTE_CONVERSATION, this.mute);
|
||||
this.$emitter.off(CMD_UNMUTE_CONVERSATION, this.unmute);
|
||||
this.$emitter.off(CMD_SEND_TRANSCRIPT, this.toggleEmailActionsModal);
|
||||
emitter.off(CMD_MUTE_CONVERSATION, this.mute);
|
||||
emitter.off(CMD_UNMUTE_CONVERSATION, this.unmute);
|
||||
emitter.off(CMD_SEND_TRANSCRIPT, this.toggleEmailActionsModal);
|
||||
},
|
||||
methods: {
|
||||
mute() {
|
||||
|
||||
@@ -31,7 +31,9 @@ defineProps({
|
||||
<div
|
||||
class="h-full w-full bg-white dark:bg-slate-900 border border-slate-100 dark:border-white/10 rounded-lg p-4 flex flex-col"
|
||||
>
|
||||
<img :src="imageSrc" :alt="imageAlt" class="h-36 w-auto mx-auto" />
|
||||
<div class="flex-1 flex items-center justify-center">
|
||||
<img :src="imageSrc" :alt="imageAlt" class="h-36 w-auto mx-auto" />
|
||||
</div>
|
||||
<div class="mt-auto">
|
||||
<p
|
||||
class="text-base text-slate-800 dark:text-slate-100 font-interDisplay font-semibold tracking-[0.3px]"
|
||||
@@ -47,7 +49,7 @@ defineProps({
|
||||
class="no-underline text-woot-500 text-sm font-medium"
|
||||
>
|
||||
<span>{{ linkText }}</span>
|
||||
<span>{{ `→` }}</span>
|
||||
<span class="ml-2">{{ `→` }}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import OnboardingFeatureCard from './OnboardingFeatureCard.vue';
|
||||
import { computed } from 'vue';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStoreGetters } from 'dashboard/composables/store';
|
||||
|
||||
const getters = useStoreGetters();
|
||||
@@ -28,7 +28,7 @@ const greetingMessage = computed(() => {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="min-h-screen max-w-4xl mx-auto grid grid-cols-1 xl:grid-cols-2 grid-rows-[auto_1fr_1fr] gap-4 p-8 w-full font-inter overflow-auto"
|
||||
class="min-h-screen lg:max-w-5xl max-w-4xl mx-auto grid grid-cols-2 grid-rows-[auto_1fr_1fr] auto-rows-min gap-4 p-8 w-full font-inter overflow-auto"
|
||||
>
|
||||
<div class="col-span-full self-start">
|
||||
<p
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script>
|
||||
import { defineAsyncComponent } from 'vue';
|
||||
import { mapGetters } from 'vuex';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useUISettings } from 'dashboard/composables/useUISettings';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
import keyboardEventListenerMixins from 'shared/mixins/keyboardEventListenerMixins';
|
||||
|
||||
import CannedResponse from './CannedResponse.vue';
|
||||
@@ -12,7 +14,7 @@ import ReplyTopPanel from 'dashboard/components/widgets/WootWriter/ReplyTopPanel
|
||||
import ReplyEmailHead from './ReplyEmailHead.vue';
|
||||
import ReplyBottomPanel from 'dashboard/components/widgets/WootWriter/ReplyBottomPanel.vue';
|
||||
import ArticleSearchPopover from 'dashboard/routes/dashboard/helpcenter/components/ArticleSearch/SearchPopover.vue';
|
||||
import MessageSignatureMissingAlert from './MessageSignatureMissingAlert';
|
||||
import MessageSignatureMissingAlert from './MessageSignatureMissingAlert.vue';
|
||||
import Banner from 'dashboard/components/ui/Banner.vue';
|
||||
import { REPLY_EDITOR_MODES } from 'dashboard/components/widgets/WootWriter/constants';
|
||||
import WootMessageEditor from 'dashboard/components/widgets/WootWriter/Editor.vue';
|
||||
@@ -40,8 +42,10 @@ import {
|
||||
|
||||
import { LOCAL_STORAGE_KEYS } from 'dashboard/constants/localStorage';
|
||||
import { LocalStorage } from 'shared/helpers/localStorage';
|
||||
|
||||
const EmojiInput = () => import('shared/components/emoji/EmojiInput.vue');
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
const EmojiInput = defineAsyncComponent(
|
||||
() => import('shared/components/emoji/EmojiInput.vue')
|
||||
);
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@@ -449,15 +453,12 @@ export default {
|
||||
);
|
||||
|
||||
this.fetchAndSetReplyTo();
|
||||
this.$emitter.on(
|
||||
BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE,
|
||||
this.fetchAndSetReplyTo
|
||||
);
|
||||
emitter.on(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE, this.fetchAndSetReplyTo);
|
||||
|
||||
// A hacky fix to solve the drag and drop
|
||||
// Is showing on top of new conversation modal drag and drop
|
||||
// TODO need to find a better solution
|
||||
this.$emitter.on(
|
||||
emitter.on(
|
||||
BUS_EVENTS.NEW_CONVERSATION_MODAL,
|
||||
this.onNewConversationModalActive
|
||||
);
|
||||
@@ -465,13 +466,10 @@ export default {
|
||||
destroyed() {
|
||||
document.removeEventListener('paste', this.onPaste);
|
||||
document.removeEventListener('keydown', this.handleKeyEvents);
|
||||
this.$emitter.off(
|
||||
BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE,
|
||||
this.fetchAndSetReplyTo
|
||||
);
|
||||
emitter.off(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE, this.fetchAndSetReplyTo);
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.$emitter.off(
|
||||
unmounted() {
|
||||
emitter.off(
|
||||
BUS_EVENTS.NEW_CONVERSATION_MODAL,
|
||||
this.onNewConversationModalActive
|
||||
);
|
||||
@@ -484,7 +482,7 @@ export default {
|
||||
const lines = title.split('\n');
|
||||
const nonEmptyLines = lines.filter(line => line.trim() !== '');
|
||||
const filteredMarkdown = nonEmptyLines.join(' ');
|
||||
this.$emitter.emit(
|
||||
emitter.emit(
|
||||
BUS_EVENTS.INSERT_INTO_RICH_EDITOR,
|
||||
`[${filteredMarkdown}](${url})`
|
||||
);
|
||||
@@ -494,7 +492,7 @@ export default {
|
||||
);
|
||||
}
|
||||
|
||||
this.$track(CONVERSATION_EVENTS.INSERT_ARTICLE_LINK);
|
||||
useTrack(CONVERSATION_EVENTS.INSERT_ARTICLE_LINK);
|
||||
},
|
||||
toggleRichContentEditor() {
|
||||
this.updateUISettings({
|
||||
@@ -689,8 +687,8 @@ export default {
|
||||
sendMessageAnalyticsData(isPrivate) {
|
||||
// Analytics data for message signature is enabled or not in channels
|
||||
return isPrivate
|
||||
? this.$track(CONVERSATION_EVENTS.SENT_PRIVATE_NOTE)
|
||||
: this.$track(CONVERSATION_EVENTS.SENT_MESSAGE, {
|
||||
? useTrack(CONVERSATION_EVENTS.SENT_PRIVATE_NOTE)
|
||||
: useTrack(CONVERSATION_EVENTS.SENT_MESSAGE, {
|
||||
channelType: this.channelType,
|
||||
signatureEnabled: this.sendWithSignature,
|
||||
hasReplyTo: !!this.inReplyTo?.id,
|
||||
@@ -726,8 +724,8 @@ export default {
|
||||
'createPendingMessageAndSend',
|
||||
messagePayload
|
||||
);
|
||||
this.$emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||
this.$emitter.emit(BUS_EVENTS.MESSAGE_SENT);
|
||||
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE);
|
||||
emitter.emit(BUS_EVENTS.MESSAGE_SENT);
|
||||
this.removeFromDraft();
|
||||
this.sendMessageAnalyticsData(messagePayload.private);
|
||||
} catch (error) {
|
||||
@@ -757,7 +755,7 @@ export default {
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
this.$track(CONVERSATION_EVENTS.INSERTED_A_CANNED_RESPONSE);
|
||||
useTrack(CONVERSATION_EVENTS.INSERTED_A_CANNED_RESPONSE);
|
||||
this.message = updatedMessage;
|
||||
}, 100);
|
||||
},
|
||||
@@ -832,7 +830,7 @@ export default {
|
||||
if (this.isRecordingAudio) {
|
||||
if (!this.isRecorderAudioStopped) {
|
||||
this.isRecorderAudioStopped = true;
|
||||
this.$refs.audioRecorderInput.stopAudioRecording();
|
||||
this.$refs.audioRecorderInput.stopRecording();
|
||||
} else if (this.isRecorderAudioStopped) {
|
||||
this.$refs.audioRecorderInput.playPause();
|
||||
}
|
||||
@@ -859,7 +857,7 @@ export default {
|
||||
onFocus() {
|
||||
this.isFocused = true;
|
||||
},
|
||||
onStateProgressRecorderChanged(duration) {
|
||||
onRecordProgressChanged(duration) {
|
||||
this.recordingAudioDurationText = duration;
|
||||
},
|
||||
onStateRecorderChanged(state) {
|
||||
@@ -1053,7 +1051,7 @@ export default {
|
||||
resetReplyToMessage() {
|
||||
const replyStorageKey = LOCAL_STORAGE_KEYS.MESSAGE_REPLY_TO;
|
||||
LocalStorage.deleteFromJsonStore(replyStorageKey, this.conversationId);
|
||||
this.$emitter.emit(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE);
|
||||
emitter.emit(BUS_EVENTS.TOGGLE_REPLY_TO_MESSAGE);
|
||||
},
|
||||
onNewConversationModalActive(isActive) {
|
||||
// Issue is if the new conversation modal is open and we drag and drop the file
|
||||
@@ -1083,7 +1081,7 @@ export default {
|
||||
:banner-message="$t('CONVERSATION.NOT_ASSIGNED_TO_YOU')"
|
||||
has-action-button
|
||||
:action-button-label="$t('CONVERSATION.ASSIGN_TO_ME')"
|
||||
@click="onClickSelfAssign"
|
||||
@primaryAction="onClickSelfAssign"
|
||||
/>
|
||||
<ReplyTopPanel
|
||||
:mode="replyType"
|
||||
@@ -1091,7 +1089,7 @@ export default {
|
||||
:characters-remaining="charactersRemaining"
|
||||
:popout-reply-box="popoutReplyBox"
|
||||
@setReplyMode="setReplyMode"
|
||||
@click="$emit('click')"
|
||||
@togglePopout="$emit('togglePopout')"
|
||||
/>
|
||||
<ArticleSearchPopover
|
||||
v-if="showArticleSearchPopover && connectedPortalSlug"
|
||||
@@ -1110,7 +1108,7 @@ export default {
|
||||
v-on-clickaway="hideMentions"
|
||||
class="normal-editor__canned-box"
|
||||
:search-key="mentionSearchKey"
|
||||
@click="replaceText"
|
||||
@replace="replaceText"
|
||||
/>
|
||||
<EmojiInput
|
||||
v-if="showEmojiPicker"
|
||||
@@ -1120,15 +1118,15 @@ export default {
|
||||
/>
|
||||
<ReplyEmailHead
|
||||
v-if="showReplyHead"
|
||||
:cc-emails.sync="ccEmails"
|
||||
:bcc-emails.sync="bccEmails"
|
||||
:to-emails.sync="toEmails"
|
||||
v-model:cc-emails="ccEmails"
|
||||
v-model:bcc-emails="bccEmails"
|
||||
v-model:to-emails="toEmails"
|
||||
/>
|
||||
<WootAudioRecorder
|
||||
v-if="showAudioRecorderEditor"
|
||||
ref="audioRecorderInput"
|
||||
:audio-record-format="audioRecordFormat"
|
||||
@stateRecorderProgressChanged="onStateProgressRecorderChanged"
|
||||
@recorderProgressChanged="onRecordProgressChanged"
|
||||
@stateRecorderChanged="onStateRecorderChanged"
|
||||
@finishRecord="onFinishRecorder"
|
||||
/>
|
||||
|
||||
@@ -90,7 +90,7 @@ export default {
|
||||
</label>
|
||||
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
|
||||
<woot-input
|
||||
v-model.trim="v$.toEmailsVal.$model"
|
||||
v-model="v$.toEmailsVal.$model"
|
||||
type="text"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
|
||||
:class="{ error: v$.toEmailsVal.$error }"
|
||||
@@ -107,7 +107,7 @@ export default {
|
||||
</label>
|
||||
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
|
||||
<woot-input
|
||||
v-model.trim="v$.ccEmailsVal.$model"
|
||||
v-model="v$.ccEmailsVal.$model"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
|
||||
type="text"
|
||||
:class="{ error: v$.ccEmailsVal.$error }"
|
||||
@@ -135,7 +135,7 @@ export default {
|
||||
</label>
|
||||
<div class="rounded-none flex-1 min-w-0 m-0 whitespace-nowrap">
|
||||
<woot-input
|
||||
v-model.trim="v$.bccEmailsVal.$model"
|
||||
v-model="v$.bccEmailsVal.$model"
|
||||
type="text"
|
||||
class="[&>input]:!mb-0 [&>input]:border-transparent [&>input]:h-8 [&>input]:text-sm [&>input]:!border-0 [&>input]:border-none"
|
||||
:class="{ error: v$.bccEmailsVal.$error }"
|
||||
|
||||
@@ -10,7 +10,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['click']);
|
||||
const emit = defineEmits(['selectAgent']);
|
||||
|
||||
const getters = useStoreGetters();
|
||||
const agents = computed(() => getters['agents/getVerifiedAgents'].value);
|
||||
@@ -36,7 +36,7 @@ const adjustScroll = () => {
|
||||
};
|
||||
|
||||
const onSelect = () => {
|
||||
emit('click', items.value[selectedIndex.value]);
|
||||
emit('selectAgent', items.value[selectedIndex.value]);
|
||||
};
|
||||
|
||||
useKeyboardNavigableList({
|
||||
|
||||
@@ -50,7 +50,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
handleVariableClick(item = {}) {
|
||||
this.$emit('click', item.key);
|
||||
this.$emit('selectVariable', item.key);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
+2
@@ -179,9 +179,11 @@ footer {
|
||||
@apply ml-2.5;
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
@apply bg-red-100 dark:bg-red-100 rounded-md text-red-800 dark:text-red-800 p-2.5 text-center;
|
||||
}
|
||||
|
||||
.template-input {
|
||||
@apply bg-slate-25 dark:bg-slate-900 text-slate-700 dark:text-slate-100;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ export default {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import '~dashboard/assets/scss/variables';
|
||||
@import 'dashboard/assets/scss/variables';
|
||||
|
||||
.file {
|
||||
display: flex;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import MessagePreview from 'dashboard/components/widgets/conversation/MessagePreview.vue';
|
||||
import { MESSAGE_TYPE } from 'shared/constants/messages';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
|
||||
export default {
|
||||
name: 'ReplyTo',
|
||||
@@ -27,7 +28,7 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
scrollToMessage() {
|
||||
this.$emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE, {
|
||||
emitter.emit(BUS_EVENTS.SCROLL_TO_MESSAGE, {
|
||||
messageId: this.message.id,
|
||||
});
|
||||
},
|
||||
|
||||
+1
-1
@@ -74,7 +74,7 @@ export default {
|
||||
this.updateSlaStatus();
|
||||
this.createTimer();
|
||||
},
|
||||
beforeDestroy() {
|
||||
unmounted() {
|
||||
if (this.timer) {
|
||||
clearTimeout(this.timer);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import MenuItem from './menuItem.vue';
|
||||
import MenuItemWithSubmenu from './menuItemWithSubmenu.vue';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import AgentLoadingPlaceholder from './agentLoadingPlaceholder.vue';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
MenuItem,
|
||||
@@ -192,7 +193,7 @@ export default {
|
||||
v-if="!hasUnreadMessages"
|
||||
:option="unreadOption"
|
||||
variant="icon"
|
||||
@click="$emit('markAsUnread')"
|
||||
@click.stop="$emit('markAsUnread')"
|
||||
/>
|
||||
<template v-for="option in statusMenuConfig">
|
||||
<MenuItem
|
||||
@@ -200,14 +201,14 @@ export default {
|
||||
:key="option.key"
|
||||
:option="option"
|
||||
variant="icon"
|
||||
@click="toggleStatus(option.key, null)"
|
||||
@click.stop="toggleStatus(option.key, null)"
|
||||
/>
|
||||
</template>
|
||||
<MenuItem
|
||||
v-if="showSnooze"
|
||||
:option="snoozeOption"
|
||||
variant="icon"
|
||||
@click="snoozeConversation()"
|
||||
@click.stop="snoozeConversation()"
|
||||
/>
|
||||
|
||||
<MenuItemWithSubmenu :option="priorityConfig">
|
||||
@@ -215,7 +216,7 @@ export default {
|
||||
v-for="(option, i) in priorityConfig.options"
|
||||
:key="i"
|
||||
:option="option"
|
||||
@click="assignPriority(option.key)"
|
||||
@click.stop="assignPriority(option.key)"
|
||||
/>
|
||||
</MenuItemWithSubmenu>
|
||||
<MenuItemWithSubmenu
|
||||
@@ -227,7 +228,7 @@ export default {
|
||||
:key="label.id"
|
||||
:option="generateMenuLabelConfig(label, 'label')"
|
||||
variant="label"
|
||||
@click="$emit('assignLabel', label)"
|
||||
@click.stop="$emit('assignLabel', label)"
|
||||
/>
|
||||
</MenuItemWithSubmenu>
|
||||
<MenuItemWithSubmenu
|
||||
@@ -241,7 +242,7 @@ export default {
|
||||
:key="agent.id"
|
||||
:option="generateMenuLabelConfig(agent, 'agent')"
|
||||
variant="agent"
|
||||
@click="$emit('assignAgent', agent)"
|
||||
@click.stop="$emit('assignAgent', agent)"
|
||||
/>
|
||||
</template>
|
||||
</MenuItemWithSubmenu>
|
||||
@@ -253,7 +254,7 @@ export default {
|
||||
v-for="team in teams"
|
||||
:key="team.id"
|
||||
:option="generateMenuLabelConfig(team, 'team')"
|
||||
@click="$emit('assignTeam', team)"
|
||||
@click.stop="$emit('assignTeam', team)"
|
||||
/>
|
||||
</MenuItemWithSubmenu>
|
||||
</div>
|
||||
|
||||
@@ -18,11 +18,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="menu text-slate-800 dark:text-slate-100"
|
||||
role="button"
|
||||
@click.stop="$emit('click')"
|
||||
>
|
||||
<div class="menu text-slate-800 dark:text-slate-100" role="button">
|
||||
<fluent-icon
|
||||
v-if="variant === 'icon' && option.icon"
|
||||
:icon="option.icon"
|
||||
|
||||
+2
-1
@@ -5,6 +5,7 @@ import Avatar from '../../Avatar.vue';
|
||||
|
||||
// composables
|
||||
import { useAI } from 'dashboard/composables/useAI';
|
||||
import { useTrack } from 'dashboard/composables';
|
||||
|
||||
// store & api
|
||||
import { mapGetters } from 'vuex';
|
||||
@@ -143,7 +144,7 @@ export default {
|
||||
: this.suggestedLabels,
|
||||
};
|
||||
|
||||
this.$track(event, payload);
|
||||
useTrack(event, payload);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
+7
-6
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import { getUnixTime } from 'date-fns';
|
||||
import { findSnoozeTime } from 'dashboard/helper/snoozeHelpers';
|
||||
import { emitter } from 'shared/helpers/mitt';
|
||||
import wootConstants from 'dashboard/constants/globals';
|
||||
import {
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
@@ -58,29 +59,29 @@ export default {
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.$emitter.on(
|
||||
emitter.on(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
this.onCmdSnoozeConversation
|
||||
);
|
||||
this.$emitter.on(
|
||||
emitter.on(
|
||||
CMD_BULK_ACTION_REOPEN_CONVERSATION,
|
||||
this.onCmdReopenConversation
|
||||
);
|
||||
this.$emitter.on(
|
||||
emitter.on(
|
||||
CMD_BULK_ACTION_RESOLVE_CONVERSATION,
|
||||
this.onCmdResolveConversation
|
||||
);
|
||||
},
|
||||
destroyed() {
|
||||
this.$emitter.off(
|
||||
emitter.off(
|
||||
CMD_BULK_ACTION_SNOOZE_CONVERSATION,
|
||||
this.onCmdSnoozeConversation
|
||||
);
|
||||
this.$emitter.off(
|
||||
emitter.off(
|
||||
CMD_BULK_ACTION_REOPEN_CONVERSATION,
|
||||
this.onCmdReopenConversation
|
||||
);
|
||||
this.$emitter.off(
|
||||
emitter.off(
|
||||
CMD_BULK_ACTION_RESOLVE_CONVERSATION,
|
||||
this.onCmdResolveConversation
|
||||
);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ref } from 'vue';
|
||||
|
||||
import WootDropdownItem from 'shared/components/ui/dropdown/DropdownItem.vue';
|
||||
|
||||
+56
-73
@@ -1,82 +1,67 @@
|
||||
<script>
|
||||
import { computed } from 'vue';
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted, nextTick, useSlots } from 'vue';
|
||||
import { useMapGetter } from 'dashboard/composables/store';
|
||||
|
||||
export default {
|
||||
props: {
|
||||
conversationLabels: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
const props = defineProps({
|
||||
conversationLabels: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
setup(props) {
|
||||
const accountLabels = useMapGetter('labels/getLabels');
|
||||
});
|
||||
|
||||
const activeLabels = computed(() => {
|
||||
return accountLabels.value.filter(({ title }) =>
|
||||
props.conversationLabels.includes(title)
|
||||
);
|
||||
});
|
||||
const slots = useSlots();
|
||||
const accountLabels = useMapGetter('labels/getLabels');
|
||||
|
||||
return {
|
||||
activeLabels,
|
||||
};
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
showAllLabels: false,
|
||||
showExpandLabelButton: false,
|
||||
labelPosition: -1,
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
activeLabels() {
|
||||
this.$nextTick(() => this.computeVisibleLabelPosition());
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
// the problem here is that there is a certain amount of delay between the conversation
|
||||
// card being mounted and the resize event eventually being triggered
|
||||
// This means we need to run the function immediately after the component is mounted
|
||||
// Happens especially when used in a virtual list.
|
||||
// We can make the first trigger, a standard part of the directive, in case
|
||||
// we face this issue again
|
||||
this.computeVisibleLabelPosition();
|
||||
},
|
||||
methods: {
|
||||
onShowLabels(e) {
|
||||
e.stopPropagation();
|
||||
this.showAllLabels = !this.showAllLabels;
|
||||
this.$nextTick(() => this.computeVisibleLabelPosition());
|
||||
},
|
||||
computeVisibleLabelPosition() {
|
||||
const beforeSlot = this.$slots.before ? 100 : 0;
|
||||
const labelContainer = this.$refs.labelContainer;
|
||||
if (!labelContainer) return;
|
||||
const activeLabels = computed(() => {
|
||||
return accountLabels.value.filter(({ title }) =>
|
||||
props.conversationLabels.includes(title)
|
||||
);
|
||||
});
|
||||
|
||||
const labels = Array.from(labelContainer.querySelectorAll('.label'));
|
||||
let labelOffset = 0;
|
||||
this.showExpandLabelButton = false;
|
||||
labels.forEach((label, index) => {
|
||||
labelOffset += label.offsetWidth + 8;
|
||||
if (labelOffset < labelContainer.clientWidth - 16 - beforeSlot) {
|
||||
this.labelPosition = index;
|
||||
} else {
|
||||
this.showExpandLabelButton = labels.length > 1;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
const showAllLabels = ref(false);
|
||||
const showExpandLabelButton = ref(false);
|
||||
const labelPosition = ref(-1);
|
||||
const labelContainer = ref(null);
|
||||
|
||||
const computeVisibleLabelPosition = () => {
|
||||
const beforeSlot = slots.before ? 100 : 0;
|
||||
if (!labelContainer.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = Array.from(labelContainer.value.querySelectorAll('.label'));
|
||||
let labelOffset = 0;
|
||||
showExpandLabelButton.value = false;
|
||||
labels.forEach((label, index) => {
|
||||
labelOffset += label.offsetWidth + 8;
|
||||
|
||||
if (labelOffset < labelContainer.value.clientWidth - beforeSlot) {
|
||||
labelPosition.value = index;
|
||||
} else {
|
||||
showExpandLabelButton.value = labels.length > 1;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
watch(activeLabels, () => {
|
||||
nextTick(() => computeVisibleLabelPosition());
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
computeVisibleLabelPosition();
|
||||
});
|
||||
|
||||
const onShowLabels = e => {
|
||||
e.stopPropagation();
|
||||
showAllLabels.value = !showAllLabels.value;
|
||||
nextTick(() => computeVisibleLabelPosition());
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
v-if="activeLabels.length || $slots.before"
|
||||
ref="labelContainer"
|
||||
v-resize="computeVisibleLabelPosition"
|
||||
>
|
||||
<div ref="labelContainer" v-resize="computeVisibleLabelPosition">
|
||||
<div
|
||||
v-if="activeLabels.length || $slots.before"
|
||||
class="flex items-end flex-shrink min-w-0 gap-y-1"
|
||||
:class="{ 'h-auto overflow-visible flex-row flex-wrap': showAllLabels }"
|
||||
>
|
||||
@@ -90,7 +75,9 @@ export default {
|
||||
variant="smooth"
|
||||
class="!mb-0 max-w-[calc(100%-0.5rem)]"
|
||||
small
|
||||
:class="{ hidden: !showAllLabels && index > labelPosition }"
|
||||
:class="{
|
||||
'invisible absolute': !showAllLabels && index > labelPosition,
|
||||
}"
|
||||
/>
|
||||
<woot-button
|
||||
v-if="showExpandLabelButton"
|
||||
@@ -123,8 +110,4 @@ export default {
|
||||
@apply border border-solid border-slate-100 dark:border-slate-700;
|
||||
}
|
||||
}
|
||||
|
||||
.hidden {
|
||||
@apply invisible absolute;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup>
|
||||
import { reactive, computed, onMounted, ref } from 'vue';
|
||||
import { useVuelidate } from '@vuelidate/core';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import LinearAPI from 'dashboard/api/integrations/linear';
|
||||
import validations from './validations';
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, ref } from 'vue';
|
||||
import LinkIssue from './LinkIssue.vue';
|
||||
import CreateIssue from './CreateIssue.vue';
|
||||
@@ -65,8 +65,9 @@ const onClickTabChange = index => {
|
||||
@change="onClickTabChange"
|
||||
>
|
||||
<woot-tabs-item
|
||||
v-for="tab in tabs"
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="tab.key"
|
||||
:index="index"
|
||||
:name="tab.name"
|
||||
:show-badge="false"
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import LinearAPI from 'dashboard/api/integrations/linear';
|
||||
import FilterButton from 'dashboard/components/ui/Dropdown/DropdownButton.vue';
|
||||
@@ -121,7 +121,7 @@ const linkIssue = async () => {
|
||||
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"
|
||||
@click="onSelectIssue"
|
||||
@select="onSelectIssue"
|
||||
/>
|
||||
</template>
|
||||
</FilterButton>
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
<script setup>
|
||||
import { ref, computed, defineComponent } from 'vue';
|
||||
import { ref, computed, defineOptions } from 'vue';
|
||||
import FilterButton from 'dashboard/components/ui/Dropdown/DropdownButton.vue';
|
||||
import FilterListDropdown from 'dashboard/components/ui/Dropdown/DropdownList.vue';
|
||||
|
||||
@@ -14,7 +14,7 @@ const props = defineProps({
|
||||
|
||||
const emit = defineEmits(['change']);
|
||||
|
||||
defineComponent({
|
||||
defineOptions({
|
||||
name: 'SearchableDropdown',
|
||||
});
|
||||
|
||||
@@ -64,7 +64,7 @@ const selectedItemId = computed(() => selectedItem.value?.id || null);
|
||||
:input-placeholder="placeholder"
|
||||
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"
|
||||
@click="onSelect"
|
||||
@select="onSelect"
|
||||
/>
|
||||
</template>
|
||||
</FilterButton>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup>
|
||||
import { computed, ref, onMounted, watch, defineComponent, provide } from 'vue';
|
||||
import { computed, ref, onMounted, watch, defineOptions, provide } from 'vue';
|
||||
import { useAlert } from 'dashboard/composables';
|
||||
import { useStoreGetters } from 'dashboard/composables/store';
|
||||
import { useI18n } from 'dashboard/composables/useI18n';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import LinearAPI from 'dashboard/api/integrations/linear';
|
||||
import CreateOrLinkIssue from './CreateOrLinkIssue.vue';
|
||||
import Issue from './Issue.vue';
|
||||
@@ -15,7 +15,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
defineComponent({
|
||||
defineOptions({
|
||||
name: 'Linear',
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { createLocalVue, mount } from '@vue/test-utils';
|
||||
import Vuex from 'vuex';
|
||||
import VueI18n from 'vue-i18n';
|
||||
import VTooltip from 'v-tooltip';
|
||||
import FloatingVue from 'floating-vue';
|
||||
import Button from 'dashboard/components/buttons/Button.vue';
|
||||
import i18n from 'dashboard/i18n';
|
||||
import FluentIcon from 'shared/components/FluentIcon/DashboardIcon.vue';
|
||||
@@ -20,7 +20,7 @@ import { emitter } from 'shared/helpers/mitt';
|
||||
const localVue = createLocalVue();
|
||||
localVue.use(Vuex);
|
||||
localVue.use(VueI18n);
|
||||
localVue.use(VTooltip);
|
||||
localVue.use(FloatingVue);
|
||||
|
||||
localVue.component('fluent-icon', FluentIcon);
|
||||
localVue.component('woot-button', Button);
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
import ContactBubble from '../bubble/Contact.vue';
|
||||
|
||||
export default {
|
||||
title: 'Components/Messaging/ContactBubble',
|
||||
component: ContactBubble,
|
||||
argTypes: {
|
||||
name: {
|
||||
defaultValue: 'Eden Hazard',
|
||||
control: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
phoneNumber: {
|
||||
defaultValue: '+517554433220',
|
||||
control: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { ContactBubble },
|
||||
template: '<contact-bubble v-bind="$props" />',
|
||||
});
|
||||
|
||||
export const ContactBubbleView = Template.bind({});
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
import LocationBubble from '../bubble/Location.vue';
|
||||
|
||||
export default {
|
||||
title: 'Components/Help Center',
|
||||
component: LocationBubble,
|
||||
argTypes: {
|
||||
latitude: {
|
||||
defaultValue: 1,
|
||||
control: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
longitude: {
|
||||
defaultValue: 1,
|
||||
control: {
|
||||
type: 'number',
|
||||
},
|
||||
},
|
||||
name: {
|
||||
defaultValue: '420, Dope street',
|
||||
control: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { LocationBubble },
|
||||
template: '<location-bubble v-bind="$props" />',
|
||||
});
|
||||
|
||||
export const LocationBubbleView = Template.bind({});
|
||||
-27
@@ -1,27 +0,0 @@
|
||||
import ReplyEmailHead from '../ReplyEmailHead';
|
||||
|
||||
export default {
|
||||
title: 'Components/ReplyBox/EmailHead',
|
||||
component: ReplyEmailHead,
|
||||
argTypes: {
|
||||
ccEmails: {
|
||||
control: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
bccEmails: {
|
||||
control: {
|
||||
type: 'string',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const Template = (args, { argTypes }) => ({
|
||||
props: Object.keys(argTypes),
|
||||
components: { ReplyEmailHead },
|
||||
template:
|
||||
'<reply-email-head v-bind="$props" @add="onAdd" @click="onClick"></reply-email-head>',
|
||||
});
|
||||
|
||||
export const Add = Template.bind({});
|
||||
Reference in New Issue
Block a user