feat(v4): Add new contact details screen (#10504)
Co-authored-by: Pranav <pranavrajs@gmail.com>
This commit is contained in:
@@ -1,123 +1,149 @@
|
||||
<script>
|
||||
import { mapGetters } from 'vuex';
|
||||
import ContactInfoPanel from '../components/ContactInfoPanel.vue';
|
||||
import ContactNotes from 'dashboard/modules/notes/NotesOnContactPage.vue';
|
||||
import SettingsHeader from '../../settings/SettingsHeader.vue';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
import Thumbnail from 'dashboard/components/widgets/Thumbnail.vue';
|
||||
<script setup>
|
||||
import { onMounted, computed, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useStore, useMapGetter } from 'dashboard/composables/store';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ContactInfoPanel,
|
||||
ContactNotes,
|
||||
SettingsHeader,
|
||||
Spinner,
|
||||
Thumbnail,
|
||||
},
|
||||
props: {
|
||||
contactId: {
|
||||
type: [String, Number],
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTabIndex: 0,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapGetters({
|
||||
uiFlags: 'contacts/getUIFlags',
|
||||
}),
|
||||
tabs() {
|
||||
return [
|
||||
{
|
||||
key: 0,
|
||||
name: this.$t('NOTES.HEADER.TITLE'),
|
||||
},
|
||||
];
|
||||
},
|
||||
showEmptySearchResult() {
|
||||
const hasEmptyResults = !!this.searchQuery && this.records.length === 0;
|
||||
return hasEmptyResults;
|
||||
},
|
||||
contact() {
|
||||
return this.$store.getters['contacts/getContact'](this.contactId);
|
||||
},
|
||||
backUrl() {
|
||||
if (window.history.state?.back || window.history.length > 1) {
|
||||
return '';
|
||||
}
|
||||
return `/app/accounts/${this.$route.params.accountId}/contacts`;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.fetchContactDetails();
|
||||
},
|
||||
methods: {
|
||||
onClickTabChange(index) {
|
||||
this.selectedTabIndex = index;
|
||||
},
|
||||
fetchContactDetails() {
|
||||
const { contactId: id } = this;
|
||||
this.$store.dispatch('contacts/show', { id });
|
||||
},
|
||||
},
|
||||
import ContactsDetailsLayout from 'dashboard/components-next/Contacts/ContactsDetailsLayout.vue';
|
||||
import Spinner from 'dashboard/components-next/spinner/Spinner.vue';
|
||||
import ContactDetails from 'dashboard/components-next/Contacts/Pages/ContactDetails.vue';
|
||||
import TabBar from 'dashboard/components-next/tabbar/TabBar.vue';
|
||||
import ContactNotes from 'dashboard/components-next/Contacts/ContactsSidebar/ContactNotes.vue';
|
||||
import ContactHistory from 'dashboard/components-next/Contacts/ContactsSidebar/ContactHistory.vue';
|
||||
import ContactMerge from 'dashboard/components-next/Contacts/ContactsSidebar/ContactMerge.vue';
|
||||
import ContactCustomAttributes from 'dashboard/components-next/Contacts/ContactsSidebar/ContactCustomAttributes.vue';
|
||||
|
||||
const store = useStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const contact = useMapGetter('contacts/getContactById');
|
||||
const uiFlags = useMapGetter('contacts/getUIFlags');
|
||||
|
||||
const activeTab = ref('attributes');
|
||||
const contactMergeRef = ref(null);
|
||||
|
||||
const isFetchingItem = computed(() => uiFlags.value.isFetchingItem);
|
||||
const isMergingContact = computed(() => uiFlags.value.isMerging);
|
||||
|
||||
const selectedContact = computed(() => contact.value(route.params.contactId));
|
||||
|
||||
const showSpinner = computed(
|
||||
() => isFetchingItem.value || isMergingContact.value
|
||||
);
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const CONTACT_TABS_OPTIONS = [
|
||||
{ key: 'ATTRIBUTES', value: 'attributes' },
|
||||
{ key: 'HISTORY', value: 'history' },
|
||||
{ key: 'NOTES', value: 'notes' },
|
||||
{ key: 'MERGE', value: 'merge' },
|
||||
];
|
||||
|
||||
const tabs = computed(() => {
|
||||
return CONTACT_TABS_OPTIONS.map(tab => ({
|
||||
label: t(`CONTACTS_LAYOUT.SIDEBAR.TABS.${tab.key}`),
|
||||
value: tab.value,
|
||||
}));
|
||||
});
|
||||
|
||||
const activeTabIndex = computed(() => {
|
||||
return CONTACT_TABS_OPTIONS.findIndex(v => v.value === activeTab.value);
|
||||
});
|
||||
|
||||
const goToContactsList = () => {
|
||||
if (window.history.state?.back || window.history.length > 1) {
|
||||
router.back();
|
||||
} else {
|
||||
router.push(`/app/accounts/${route.params.accountId}/contacts?page=1`);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchActiveContact = async () => {
|
||||
if (route.params.contactId) {
|
||||
store.dispatch('contacts/show', { id: route.params.contactId });
|
||||
}
|
||||
};
|
||||
|
||||
const handleTabChange = tab => {
|
||||
activeTab.value = tab.value;
|
||||
};
|
||||
|
||||
const fetchContactNotes = () => {
|
||||
const { contactId } = route.params;
|
||||
if (contactId) store.dispatch('contactNotes/get', { contactId });
|
||||
};
|
||||
|
||||
const fetchContactConversations = () => {
|
||||
const { contactId } = route.params;
|
||||
if (contactId) store.dispatch('contactConversations/get', contactId);
|
||||
};
|
||||
|
||||
const fetchAttributes = () => {
|
||||
store.dispatch('attributes/get');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
fetchActiveContact();
|
||||
fetchContactNotes();
|
||||
fetchContactConversations();
|
||||
fetchAttributes();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="flex justify-between flex-col h-full m-0 flex-1 bg-white dark:bg-slate-900"
|
||||
class="flex flex-col justify-between flex-1 h-full m-0 overflow-auto bg-n-background"
|
||||
>
|
||||
<SettingsHeader
|
||||
button-route="new"
|
||||
:header-title="contact.name"
|
||||
show-back-button
|
||||
:back-button-label="$t('CONTACT_PROFILE.BACK_BUTTON')"
|
||||
:back-url="backUrl"
|
||||
:show-new-button="false"
|
||||
<ContactsDetailsLayout
|
||||
:button-label="$t('CONTACTS_LAYOUT.HEADER.MESSAGE_BUTTON')"
|
||||
:selected-contact="selectedContact"
|
||||
is-detail-view
|
||||
:show-pagination-footer="false"
|
||||
@go-to-contacts-list="goToContactsList"
|
||||
>
|
||||
<Thumbnail
|
||||
v-if="contact.thumbnail"
|
||||
:src="contact.thumbnail"
|
||||
:username="contact.name"
|
||||
size="32px"
|
||||
class="mr-2 rtl:mr-0 rtl:ml-2"
|
||||
/>
|
||||
</SettingsHeader>
|
||||
|
||||
<div v-if="uiFlags.isFetchingItem" class="text-center p-4 text-base h-full">
|
||||
<Spinner size="" />
|
||||
<span>{{ $t('CONTACT_PROFILE.LOADING') }}</span>
|
||||
</div>
|
||||
<div v-else-if="contact.id" class="overflow-hidden flex-1 min-w-0">
|
||||
<div class="flex flex-wrap ml-auto mr-auto max-w-full h-full">
|
||||
<ContactInfoPanel
|
||||
:show-close-button="false"
|
||||
:show-avatar="false"
|
||||
:contact="contact"
|
||||
/>
|
||||
<div class="w-3/4 h-full">
|
||||
<woot-tabs :index="selectedTabIndex" @change="onClickTabChange">
|
||||
<woot-tabs-item
|
||||
v-for="(tab, index) in tabs"
|
||||
:key="tab.key"
|
||||
:index="index"
|
||||
:name="tab.name"
|
||||
:show-badge="false"
|
||||
/>
|
||||
</woot-tabs>
|
||||
<div
|
||||
class="bg-slate-25 dark:bg-slate-800 h-[calc(100%-40px)] p-4 overflow-auto"
|
||||
>
|
||||
<ContactNotes
|
||||
v-if="selectedTabIndex === 0"
|
||||
:contact-id="Number(contactId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="showSpinner"
|
||||
class="flex items-center justify-center py-10 text-n-slate-11"
|
||||
>
|
||||
<Spinner />
|
||||
</div>
|
||||
</div>
|
||||
<ContactDetails
|
||||
v-else-if="selectedContact"
|
||||
:selected-contact="selectedContact"
|
||||
@go-to-contacts-list="goToContactsList"
|
||||
/>
|
||||
<template #sidebar>
|
||||
<div class="px-6">
|
||||
<TabBar
|
||||
:tabs="tabs"
|
||||
:initial-active-tab="activeTabIndex"
|
||||
class="w-full [&>button]:w-full bg-n-alpha-black2"
|
||||
@tab-changed="handleTabChange"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="isFetchingItem"
|
||||
class="flex items-center justify-center py-10 text-n-slate-11"
|
||||
>
|
||||
<Spinner />
|
||||
</div>
|
||||
<template v-else>
|
||||
<ContactCustomAttributes
|
||||
v-if="activeTab === 'attributes'"
|
||||
:selected-contact="selectedContact"
|
||||
/>
|
||||
<ContactNotes v-if="activeTab === 'notes'" />
|
||||
<ContactHistory v-if="activeTab === 'history'" />
|
||||
<ContactMerge
|
||||
v-if="activeTab === 'merge'"
|
||||
ref="contactMergeRef"
|
||||
:selected-contact="selectedContact"
|
||||
@go-to-contacts-list="goToContactsList"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</ContactsDetailsLayout>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -133,6 +133,7 @@ const fetchSavedOrAppliedFilteredContact = async (payload, page = 1) => {
|
||||
};
|
||||
|
||||
const searchContacts = debounce(async (value, page = 1) => {
|
||||
await store.dispatch('contacts/clearContactFilters');
|
||||
searchValue.value = value;
|
||||
|
||||
if (!value) {
|
||||
|
||||
@@ -1,44 +1,60 @@
|
||||
/* eslint arrow-body-style: 0 */
|
||||
import { frontendURL } from '../../../helper/URLHelper';
|
||||
import ContactsIndex from './pages/ContactsIndex.vue';
|
||||
import ContactManageView from './pages/ContactManageView.vue';
|
||||
|
||||
const commonMeta = {
|
||||
permissions: ['administrator', 'agent', 'contact_manage'],
|
||||
};
|
||||
|
||||
export const routes = [
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/contacts'),
|
||||
component: ContactsIndex,
|
||||
name: 'contacts_dashboard_index',
|
||||
meta: {
|
||||
permissions: ['administrator', 'agent', 'contact_manage'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/contacts/segments/:segmentId'),
|
||||
component: ContactsIndex,
|
||||
name: 'contacts_dashboard_segments_index',
|
||||
meta: {
|
||||
permissions: ['administrator', 'agent', 'contact_manage'],
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/contacts/labels/:label'),
|
||||
component: ContactsIndex,
|
||||
name: 'contacts_dashboard_labels_index',
|
||||
meta: {
|
||||
permissions: ['administrator', 'agent', 'contact_manage'],
|
||||
},
|
||||
meta: commonMeta,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'contacts_dashboard_index',
|
||||
component: ContactsIndex,
|
||||
meta: commonMeta,
|
||||
},
|
||||
{
|
||||
path: 'segments/:segmentId',
|
||||
name: 'contacts_dashboard_segments_index',
|
||||
component: ContactsIndex,
|
||||
meta: commonMeta,
|
||||
},
|
||||
{
|
||||
path: 'labels/:label',
|
||||
name: 'contacts_dashboard_labels_index',
|
||||
component: ContactsIndex,
|
||||
meta: commonMeta,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: frontendURL('accounts/:accountId/contacts/:contactId'),
|
||||
name: 'contacts_edit',
|
||||
meta: {
|
||||
permissions: ['administrator', 'agent', 'contact_manage'],
|
||||
},
|
||||
component: ContactManageView,
|
||||
props: route => {
|
||||
return { contactId: route.params.contactId };
|
||||
},
|
||||
meta: commonMeta,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
name: 'contacts_edit',
|
||||
component: ContactManageView,
|
||||
meta: commonMeta,
|
||||
},
|
||||
{
|
||||
path: 'segments/:segmentId',
|
||||
name: 'contacts_edit_segment',
|
||||
component: ContactManageView,
|
||||
meta: commonMeta,
|
||||
},
|
||||
{
|
||||
path: 'labels/:label',
|
||||
name: 'contacts_edit_label',
|
||||
component: ContactManageView,
|
||||
meta: commonMeta,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user