Restructure omni services and add Chatwoot research snapshot

This commit is contained in:
Ruslan Bakiev
2026-02-21 11:11:27 +07:00
parent edea7a0034
commit b73babbbf6
7732 changed files with 978203 additions and 32 deletions

View File

@@ -0,0 +1,98 @@
<script>
import { mapGetters } from 'vuex';
import { useAdmin } from 'dashboard/composables/useAdmin';
import { useAccount } from 'dashboard/composables/useAccount';
import OnboardingView from '../OnboardingView.vue';
import EmptyStateMessage from './EmptyStateMessage.vue';
export default {
components: {
OnboardingView,
EmptyStateMessage,
},
props: {
isOnExpandedLayout: {
type: Boolean,
default: false,
},
},
setup() {
const { isAdmin } = useAdmin();
const { accountScopedUrl } = useAccount();
return {
isAdmin,
accountScopedUrl,
};
},
computed: {
...mapGetters({
currentChat: 'getSelectedChat',
allConversations: 'getAllConversations',
inboxesList: 'inboxes/getInboxes',
uiFlags: 'inboxes/getUIFlags',
loadingChatList: 'getChatListLoadingStatus',
}),
loadingIndicatorMessage() {
if (this.uiFlags.isFetching) {
return this.$t('CONVERSATION.LOADING_INBOXES');
}
return this.$t('CONVERSATION.LOADING_CONVERSATIONS');
},
conversationMissingMessage() {
if (!this.isOnExpandedLayout) {
return this.$t('CONVERSATION.SELECT_A_CONVERSATION');
}
return this.$t('CONVERSATION.404');
},
newInboxURL() {
return this.accountScopedUrl('settings/inboxes/new');
},
emptyClassName() {
if (
!this.inboxesList.length &&
!this.uiFlags.isFetching &&
!this.loadingChatList &&
this.isAdmin
) {
return 'h-full overflow-auto w-full';
}
return 'flex-1 min-w-0 px-0 flex flex-col items-center justify-center h-full bg-n-surface-1';
},
},
};
</script>
<template>
<div :class="emptyClassName">
<woot-loading-state
v-if="uiFlags.isFetching || loadingChatList"
:message="loadingIndicatorMessage"
/>
<!-- No inboxes attached -->
<div
v-if="!inboxesList.length && !uiFlags.isFetching && !loadingChatList"
class="clearfix mx-auto"
>
<OnboardingView v-if="isAdmin" />
<EmptyStateMessage v-else :message="$t('CONVERSATION.NO_INBOX_AGENT')" />
</div>
<!-- Show empty state images if not loading -->
<div
v-else-if="!uiFlags.isFetching && !loadingChatList"
class="flex flex-col items-center justify-center h-full"
>
<!-- No conversations available -->
<EmptyStateMessage
v-if="!allConversations.length"
:message="$t('CONVERSATION.NO_MESSAGE_1')"
/>
<EmptyStateMessage
v-else-if="allConversations.length && !currentChat.id"
:message="conversationMissingMessage"
/>
</div>
</div>
</template>

View File

@@ -0,0 +1,33 @@
<script>
import FeaturePlaceholder from './FeaturePlaceholder.vue';
export default {
components: { FeaturePlaceholder },
props: {
message: {
type: String,
required: true,
},
},
};
</script>
<template>
<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"
alt="No Chat dark"
/>
<img
class="m-4 w-32 block dark:hidden"
src="dashboard/assets/images/no-chat.svg"
alt="No Chat"
/>
<span class="text-sm text-n-slate-12 font-medium text-center">
{{ message }}
<br />
</span>
<!-- Cmd bar, keyboard shortcuts placeholder -->
<FeaturePlaceholder />
</div>
</template>

View File

@@ -0,0 +1,49 @@
<script>
import Hotkey from 'dashboard/components/base/Hotkey.vue';
export default {
components: {
Hotkey,
},
data() {
return {
keyShortcuts: [
{
key: 'K',
description: this.$t('CONVERSATION.EMPTY_STATE.CMD_BAR'),
},
{
key: '/',
description: this.$t('CONVERSATION.EMPTY_STATE.KEYBOARD_SHORTCUTS'),
},
],
};
},
};
</script>
<template>
<div class="flex flex-col gap-2 mt-9">
<div
v-for="keyShortcut in keyShortcuts"
:key="keyShortcut.key"
class="flex items-center gap-2"
>
<div class="flex items-center gap-2">
<Hotkey
custom-class="w-8 h-6 text-lg font-medium text-n-slate-12 outline outline-n-container outline-1 bg-n-alpha-3"
>
</Hotkey>
<Hotkey
custom-class="w-8 h-6 text-xs font-medium text-n-slate-12 outline outline-n-container outline-1 bg-n-alpha-3"
>
{{ keyShortcut.key }}
</Hotkey>
</div>
<span class="text-sm font-medium text-center text-n-slate-12">
{{ keyShortcut.description }}
</span>
</div>
</div>
</template>