feat: Show active Contacts (#8243)
This commit is contained in:
parent
e9a132a923
commit
513d954027
@ -14,7 +14,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
before_action :check_authorization
|
before_action :check_authorization
|
||||||
before_action :set_current_page, only: [:index, :active, :search, :filter]
|
before_action :set_current_page, only: [:index, :active, :search, :filter]
|
||||||
before_action :fetch_contact, only: [:show, :update, :destroy, :avatar, :contactable_inboxes, :destroy_custom_attributes]
|
before_action :fetch_contact, only: [:show, :update, :destroy, :avatar, :contactable_inboxes, :destroy_custom_attributes]
|
||||||
before_action :set_include_contact_inboxes, only: [:index, :search, :filter, :show, :update]
|
before_action :set_include_contact_inboxes, only: [:index, :active, :search, :filter, :show, :update]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@contacts_count = resolved_contacts.count
|
@contacts_count = resolved_contacts.count
|
||||||
@ -56,7 +56,7 @@ class Api::V1::Accounts::ContactsController < Api::V1::Accounts::BaseController
|
|||||||
contacts = Current.account.contacts.where(id: ::OnlineStatusTracker
|
contacts = Current.account.contacts.where(id: ::OnlineStatusTracker
|
||||||
.get_available_contact_ids(Current.account.id))
|
.get_available_contact_ids(Current.account.id))
|
||||||
@contacts_count = contacts.count
|
@contacts_count = contacts.count
|
||||||
@contacts = contacts.page(@current_page)
|
@contacts = fetch_contacts(contacts)
|
||||||
end
|
end
|
||||||
|
|
||||||
def show; end
|
def show; end
|
||||||
|
|||||||
@ -61,6 +61,11 @@ class ContactAPI extends ApiClient {
|
|||||||
return axios.get(requestURL);
|
return axios.get(requestURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
active(page = 1, sortAttr = 'name') {
|
||||||
|
let requestURL = `${this.url}/active?${buildContactParams(page, sortAttr)}`;
|
||||||
|
return axios.get(requestURL);
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line default-param-last
|
// eslint-disable-next-line default-param-last
|
||||||
filter(page = 1, sortAttr = 'name', queryPayload) {
|
filter(page = 1, sortAttr = 'name', queryPayload) {
|
||||||
let requestURL = `${this.url}/filter?${buildContactParams(page, sortAttr)}`;
|
let requestURL = `${this.url}/filter?${buildContactParams(page, sortAttr)}`;
|
||||||
|
|||||||
@ -17,6 +17,7 @@ const props = defineProps({
|
|||||||
additionalAttributes: { type: Object, default: () => ({}) },
|
additionalAttributes: { type: Object, default: () => ({}) },
|
||||||
phoneNumber: { type: String, default: '' },
|
phoneNumber: { type: String, default: '' },
|
||||||
thumbnail: { type: String, default: '' },
|
thumbnail: { type: String, default: '' },
|
||||||
|
availabilityStatus: { type: String, default: null },
|
||||||
isExpanded: { type: Boolean, default: false },
|
isExpanded: { type: Boolean, default: false },
|
||||||
isUpdating: { type: Boolean, default: false },
|
isUpdating: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
@ -92,7 +93,13 @@ const onClickViewDetails = () => emit('showContact', props.id);
|
|||||||
<template>
|
<template>
|
||||||
<CardLayout :key="id" layout="row">
|
<CardLayout :key="id" layout="row">
|
||||||
<div class="flex items-center justify-start flex-1 gap-4">
|
<div class="flex items-center justify-start flex-1 gap-4">
|
||||||
<Avatar :name="name" :src="thumbnail" :size="48" rounded-full />
|
<Avatar
|
||||||
|
:name="name"
|
||||||
|
:src="thumbnail"
|
||||||
|
:size="48"
|
||||||
|
:status="availabilityStatus"
|
||||||
|
rounded-full
|
||||||
|
/>
|
||||||
<div class="flex flex-col gap-0.5 flex-1">
|
<div class="flex flex-col gap-0.5 flex-1">
|
||||||
<div class="flex flex-wrap items-center gap-x-4 gap-y-1">
|
<div class="flex flex-wrap items-center gap-x-4 gap-y-1">
|
||||||
<span class="text-base font-medium truncate text-n-slate-12">
|
<span class="text-base font-medium truncate text-n-slate-12">
|
||||||
|
|||||||
@ -7,42 +7,16 @@ import ContactMoreActions from './components/ContactMoreActions.vue';
|
|||||||
import ComposeConversation from 'dashboard/components-next/NewConversation/ComposeConversation.vue';
|
import ComposeConversation from 'dashboard/components-next/NewConversation/ComposeConversation.vue';
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
showSearch: {
|
showSearch: { type: Boolean, default: true },
|
||||||
type: Boolean,
|
searchValue: { type: String, default: '' },
|
||||||
default: true,
|
headerTitle: { type: String, required: true },
|
||||||
},
|
buttonLabel: { type: String, default: '' },
|
||||||
searchValue: {
|
activeSort: { type: String, default: 'last_activity_at' },
|
||||||
type: String,
|
activeOrdering: { type: String, default: '' },
|
||||||
default: '',
|
isSegmentsView: { type: Boolean, default: false },
|
||||||
},
|
hasActiveFilters: { type: Boolean, default: false },
|
||||||
headerTitle: {
|
isLabelView: { type: Boolean, default: false },
|
||||||
type: String,
|
isActiveView: { type: Boolean, default: false },
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
buttonLabel: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
activeSort: {
|
|
||||||
type: String,
|
|
||||||
default: 'last_activity_at',
|
|
||||||
},
|
|
||||||
activeOrdering: {
|
|
||||||
type: String,
|
|
||||||
default: '',
|
|
||||||
},
|
|
||||||
isSegmentsView: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
hasActiveFilters: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
isLabelView: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@ -85,7 +59,7 @@ const emit = defineEmits([
|
|||||||
</Input>
|
</Input>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div v-if="!isLabelView" class="relative">
|
<div v-if="!isLabelView && !isActiveView" class="relative">
|
||||||
<Button
|
<Button
|
||||||
id="toggleContactsFilterButton"
|
id="toggleContactsFilterButton"
|
||||||
:icon="
|
:icon="
|
||||||
@ -105,7 +79,12 @@ const emit = defineEmits([
|
|||||||
<slot name="filter" />
|
<slot name="filter" />
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
v-if="hasActiveFilters && !isSegmentsView && !isLabelView"
|
v-if="
|
||||||
|
hasActiveFilters &&
|
||||||
|
!isSegmentsView &&
|
||||||
|
!isLabelView &&
|
||||||
|
!isActiveView
|
||||||
|
"
|
||||||
icon="i-lucide-save"
|
icon="i-lucide-save"
|
||||||
color="slate"
|
color="slate"
|
||||||
size="sm"
|
size="sm"
|
||||||
@ -113,7 +92,7 @@ const emit = defineEmits([
|
|||||||
@click="emit('createSegment')"
|
@click="emit('createSegment')"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
v-if="isSegmentsView && !isLabelView"
|
v-if="isSegmentsView && !isLabelView && !isActiveView"
|
||||||
icon="i-lucide-trash"
|
icon="i-lucide-trash"
|
||||||
color="slate"
|
color="slate"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
|||||||
@ -36,6 +36,7 @@ const props = defineProps({
|
|||||||
activeSegment: { type: Object, default: null },
|
activeSegment: { type: Object, default: null },
|
||||||
hasAppliedFilters: { type: Boolean, default: false },
|
hasAppliedFilters: { type: Boolean, default: false },
|
||||||
isLabelView: { type: Boolean, default: false },
|
isLabelView: { type: Boolean, default: false },
|
||||||
|
isActiveView: { type: Boolean, default: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const emit = defineEmits([
|
const emit = defineEmits([
|
||||||
@ -277,6 +278,7 @@ defineExpose({
|
|||||||
:header-title="headerTitle"
|
:header-title="headerTitle"
|
||||||
:is-segments-view="hasActiveSegments"
|
:is-segments-view="hasActiveSegments"
|
||||||
:is-label-view="isLabelView"
|
:is-label-view="isLabelView"
|
||||||
|
:is-active-view="isActiveView"
|
||||||
:has-active-filters="hasAppliedFilters"
|
:has-active-filters="hasAppliedFilters"
|
||||||
:button-label="t('CONTACTS_LAYOUT.HEADER.MESSAGE_BUTTON')"
|
:button-label="t('CONTACTS_LAYOUT.HEADER.MESSAGE_BUTTON')"
|
||||||
@search="emit('search', $event)"
|
@search="emit('search', $event)"
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import ContactListHeaderWrapper from 'dashboard/components-next/Contacts/Contact
|
|||||||
import ContactsActiveFiltersPreview from 'dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue';
|
import ContactsActiveFiltersPreview from 'dashboard/components-next/Contacts/ContactsHeader/components/ContactsActiveFiltersPreview.vue';
|
||||||
import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue';
|
import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue';
|
||||||
|
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
searchValue: { type: String, default: '' },
|
searchValue: { type: String, default: '' },
|
||||||
headerTitle: { type: String, default: '' },
|
headerTitle: { type: String, default: '' },
|
||||||
showPaginationFooter: { type: Boolean, default: true },
|
showPaginationFooter: { type: Boolean, default: true },
|
||||||
@ -37,10 +37,23 @@ const isNotSegmentView = computed(() => {
|
|||||||
return route.name !== 'contacts_dashboard_segments_index';
|
return route.name !== 'contacts_dashboard_segments_index';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isActiveView = computed(() => {
|
||||||
|
return route.name === 'contacts_dashboard_active';
|
||||||
|
});
|
||||||
|
|
||||||
const isLabelView = computed(
|
const isLabelView = computed(
|
||||||
() => route.name === 'contacts_dashboard_labels_index'
|
() => route.name === 'contacts_dashboard_labels_index'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const showActiveFiltersPreview = computed(() => {
|
||||||
|
return (
|
||||||
|
(props.hasAppliedFilters || !isNotSegmentView.value) &&
|
||||||
|
!props.isFetchingList &&
|
||||||
|
!isLabelView.value &&
|
||||||
|
!isActiveView.value
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const updateCurrentPage = page => {
|
const updateCurrentPage = page => {
|
||||||
emit('update:currentPage', page);
|
emit('update:currentPage', page);
|
||||||
};
|
};
|
||||||
@ -57,7 +70,7 @@ const openFilter = () => {
|
|||||||
<div class="flex flex-col w-full h-full transition-all duration-300">
|
<div class="flex flex-col w-full h-full transition-all duration-300">
|
||||||
<ContactListHeaderWrapper
|
<ContactListHeaderWrapper
|
||||||
ref="contactListHeaderWrapper"
|
ref="contactListHeaderWrapper"
|
||||||
:show-search="isNotSegmentView"
|
:show-search="isNotSegmentView && !isActiveView"
|
||||||
:search-value="searchValue"
|
:search-value="searchValue"
|
||||||
:active-sort="activeSort"
|
:active-sort="activeSort"
|
||||||
:active-ordering="activeOrdering"
|
:active-ordering="activeOrdering"
|
||||||
@ -66,6 +79,7 @@ const openFilter = () => {
|
|||||||
:segments-id="segmentsId"
|
:segments-id="segmentsId"
|
||||||
:has-applied-filters="hasAppliedFilters"
|
:has-applied-filters="hasAppliedFilters"
|
||||||
:is-label-view="isLabelView"
|
:is-label-view="isLabelView"
|
||||||
|
:is-active-view="isActiveView"
|
||||||
@update:sort="emit('update:sort', $event)"
|
@update:sort="emit('update:sort', $event)"
|
||||||
@search="emit('search', $event)"
|
@search="emit('search', $event)"
|
||||||
@apply-filter="emit('applyFilter', $event)"
|
@apply-filter="emit('applyFilter', $event)"
|
||||||
@ -74,11 +88,7 @@ const openFilter = () => {
|
|||||||
<main class="flex-1 overflow-y-auto">
|
<main class="flex-1 overflow-y-auto">
|
||||||
<div class="w-full mx-auto max-w-[60rem]">
|
<div class="w-full mx-auto max-w-[60rem]">
|
||||||
<ContactsActiveFiltersPreview
|
<ContactsActiveFiltersPreview
|
||||||
v-if="
|
v-if="showActiveFiltersPreview"
|
||||||
(hasAppliedFilters || !isNotSegmentView) &&
|
|
||||||
!isFetchingList &&
|
|
||||||
!isLabelView
|
|
||||||
"
|
|
||||||
:active-segment="activeSegment"
|
:active-segment="activeSegment"
|
||||||
@clear-filters="emit('clearFilters')"
|
@clear-filters="emit('clearFilters')"
|
||||||
@open-filter="openFilter"
|
@open-filter="openFilter"
|
||||||
|
|||||||
@ -71,6 +71,7 @@ const toggleExpanded = id => {
|
|||||||
:thumbnail="contact.thumbnail"
|
:thumbnail="contact.thumbnail"
|
||||||
:phone-number="contact.phoneNumber"
|
:phone-number="contact.phoneNumber"
|
||||||
:additional-attributes="contact.additionalAttributes"
|
:additional-attributes="contact.additionalAttributes"
|
||||||
|
:availability-status="contact.availabilityStatus"
|
||||||
:is-expanded="expandedCardId === contact.id"
|
:is-expanded="expandedCardId === contact.id"
|
||||||
:is-updating="isUpdating"
|
:is-updating="isUpdating"
|
||||||
@toggle="toggleExpanded(contact.id)"
|
@toggle="toggleExpanded(contact.id)"
|
||||||
|
|||||||
@ -235,6 +235,12 @@ const menuItems = computed(() => {
|
|||||||
),
|
),
|
||||||
activeOn: ['contacts_dashboard_index', 'contacts_edit'],
|
activeOn: ['contacts_dashboard_index', 'contacts_edit'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Active',
|
||||||
|
label: t('SIDEBAR.ACTIVE'),
|
||||||
|
to: accountScopedRoute('contacts_dashboard_active'),
|
||||||
|
activeOn: ['contacts_dashboard_active'],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Segments',
|
name: 'Segments',
|
||||||
icon: 'i-lucide-group',
|
icon: 'i-lucide-group',
|
||||||
|
|||||||
@ -9,6 +9,7 @@ const contacts = accountId => ({
|
|||||||
'contacts_edit',
|
'contacts_edit',
|
||||||
'contacts_edit_segment',
|
'contacts_edit_segment',
|
||||||
'contacts_edit_label',
|
'contacts_edit_label',
|
||||||
|
'contacts_dashboard_active',
|
||||||
],
|
],
|
||||||
menuItems: [
|
menuItems: [
|
||||||
{
|
{
|
||||||
@ -18,6 +19,13 @@ const contacts = accountId => ({
|
|||||||
toState: frontendURL(`accounts/${accountId}/contacts?page=1`),
|
toState: frontendURL(`accounts/${accountId}/contacts?page=1`),
|
||||||
toStateName: 'contacts_dashboard_index',
|
toStateName: 'contacts_dashboard_index',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
icon: 'visitor-contacts',
|
||||||
|
label: 'ACTIVE',
|
||||||
|
hasSubMenu: false,
|
||||||
|
toState: frontendURL(`accounts/${accountId}/contacts/active`),
|
||||||
|
toStateName: 'contacts_dashboard_active',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -286,6 +286,7 @@
|
|||||||
"HEADER": {
|
"HEADER": {
|
||||||
"TITLE": "Contacts",
|
"TITLE": "Contacts",
|
||||||
"SEARCH_TITLE": "Search contacts",
|
"SEARCH_TITLE": "Search contacts",
|
||||||
|
"ACTIVE_TITLE": "Active contacts",
|
||||||
"SEARCH_PLACEHOLDER": "Search...",
|
"SEARCH_PLACEHOLDER": "Search...",
|
||||||
"MESSAGE_BUTTON": "Message",
|
"MESSAGE_BUTTON": "Message",
|
||||||
"SEND_MESSAGE": "Send message",
|
"SEND_MESSAGE": "Send message",
|
||||||
@ -560,7 +561,8 @@
|
|||||||
"SUBTITLE": "Start adding new contacts by clicking on the button below",
|
"SUBTITLE": "Start adding new contacts by clicking on the button below",
|
||||||
"BUTTON_LABEL": "Add contact",
|
"BUTTON_LABEL": "Add contact",
|
||||||
"SEARCH_EMPTY_STATE_TITLE": "No contacts matches your search 🔍",
|
"SEARCH_EMPTY_STATE_TITLE": "No contacts matches your search 🔍",
|
||||||
"LIST_EMPTY_STATE_TITLE": "No contacts available in this view 📋"
|
"LIST_EMPTY_STATE_TITLE": "No contacts available in this view 📋",
|
||||||
|
"ACTIVE_EMPTY_STATE_TITLE": "No contacts are active at the moment 🌙"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@ -286,6 +286,7 @@
|
|||||||
"REPORTS": "Reports",
|
"REPORTS": "Reports",
|
||||||
"SETTINGS": "Settings",
|
"SETTINGS": "Settings",
|
||||||
"CONTACTS": "Contacts",
|
"CONTACTS": "Contacts",
|
||||||
|
"ACTIVE": "Active",
|
||||||
"CAPTAIN": "Captain",
|
"CAPTAIN": "Captain",
|
||||||
"CAPTAIN_ASSISTANTS": "Assistants",
|
"CAPTAIN_ASSISTANTS": "Assistants",
|
||||||
"CAPTAIN_DOCUMENTS": "Documents",
|
"CAPTAIN_DOCUMENTS": "Documents",
|
||||||
|
|||||||
@ -67,9 +67,11 @@ const hasContacts = computed(() => contacts.value.length > 0);
|
|||||||
const isContactIndexView = computed(
|
const isContactIndexView = computed(
|
||||||
() => route.name === 'contacts_dashboard_index' && pageNumber.value === 1
|
() => route.name === 'contacts_dashboard_index' && pageNumber.value === 1
|
||||||
);
|
);
|
||||||
|
const isActiveView = computed(() => route.name === 'contacts_dashboard_active');
|
||||||
const hasAppliedFilters = computed(() => {
|
const hasAppliedFilters = computed(() => {
|
||||||
return appliedFilters.value.length > 0;
|
return appliedFilters.value.length > 0;
|
||||||
});
|
});
|
||||||
|
|
||||||
const showEmptyStateLayout = computed(() => {
|
const showEmptyStateLayout = computed(() => {
|
||||||
return (
|
return (
|
||||||
!searchQuery.value &&
|
!searchQuery.value &&
|
||||||
@ -89,11 +91,20 @@ const showEmptyText = computed(() => {
|
|||||||
|
|
||||||
const headerTitle = computed(() => {
|
const headerTitle = computed(() => {
|
||||||
if (searchQuery.value) return t('CONTACTS_LAYOUT.HEADER.SEARCH_TITLE');
|
if (searchQuery.value) return t('CONTACTS_LAYOUT.HEADER.SEARCH_TITLE');
|
||||||
|
if (isActiveView.value) return t('CONTACTS_LAYOUT.HEADER.ACTIVE_TITLE');
|
||||||
if (activeSegmentId.value) return activeSegment.value?.name;
|
if (activeSegmentId.value) return activeSegment.value?.name;
|
||||||
if (activeLabel.value) return `#${activeLabel.value}`;
|
if (activeLabel.value) return `#${activeLabel.value}`;
|
||||||
return t('CONTACTS_LAYOUT.HEADER.TITLE');
|
return t('CONTACTS_LAYOUT.HEADER.TITLE');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const emptyStateMessage = computed(() => {
|
||||||
|
if (isActiveView.value)
|
||||||
|
return t('CONTACTS_LAYOUT.EMPTY_STATE.ACTIVE_EMPTY_STATE_TITLE');
|
||||||
|
if (!searchQuery.value || hasAppliedFilters.value)
|
||||||
|
return t('CONTACTS_LAYOUT.EMPTY_STATE.LIST_EMPTY_STATE_TITLE');
|
||||||
|
return t('CONTACTS_LAYOUT.EMPTY_STATE.SEARCH_EMPTY_STATE_TITLE');
|
||||||
|
});
|
||||||
|
|
||||||
const updatePageParam = (page, search = '') => {
|
const updatePageParam = (page, search = '') => {
|
||||||
const query = {
|
const query = {
|
||||||
...route.query,
|
...route.query,
|
||||||
@ -132,6 +143,15 @@ const fetchSavedOrAppliedFilteredContact = async (payload, page = 1) => {
|
|||||||
updatePageParam(page);
|
updatePageParam(page);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fetchActiveContacts = async (page = 1) => {
|
||||||
|
await store.dispatch('contacts/clearContactFilters');
|
||||||
|
await store.dispatch('contacts/active', {
|
||||||
|
page,
|
||||||
|
sortAttr: buildSortAttr(),
|
||||||
|
});
|
||||||
|
updatePageParam(page);
|
||||||
|
};
|
||||||
|
|
||||||
const searchContacts = debounce(async (value, page = 1) => {
|
const searchContacts = debounce(async (value, page = 1) => {
|
||||||
await store.dispatch('contacts/clearContactFilters');
|
await store.dispatch('contacts/clearContactFilters');
|
||||||
searchValue.value = value;
|
searchValue.value = value;
|
||||||
@ -158,6 +178,11 @@ const fetchContactsBasedOnContext = async page => {
|
|||||||
}
|
}
|
||||||
// Reset the search value when we change the view
|
// Reset the search value when we change the view
|
||||||
searchValue.value = '';
|
searchValue.value = '';
|
||||||
|
// If we're on the active route, fetch active contacts
|
||||||
|
if (isActiveView.value) {
|
||||||
|
await fetchActiveContacts(page);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// If there are applied filters or active segment with query
|
// If there are applied filters or active segment with query
|
||||||
if (
|
if (
|
||||||
(hasAppliedFilters.value || activeSegment.value?.query) &&
|
(hasAppliedFilters.value || activeSegment.value?.query) &&
|
||||||
@ -184,6 +209,11 @@ const handleSort = async ({ sort, order }) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isActiveView.value) {
|
||||||
|
await fetchActiveContacts();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
await (activeSegmentId.value || hasAppliedFilters.value
|
await (activeSegmentId.value || hasAppliedFilters.value
|
||||||
? fetchSavedOrAppliedFilteredContact(
|
? fetchSavedOrAppliedFilteredContact(
|
||||||
activeSegmentId.value
|
activeSegmentId.value
|
||||||
@ -210,7 +240,7 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[activeLabel, activeSegment],
|
[activeLabel, activeSegment, isActiveView],
|
||||||
() => {
|
() => {
|
||||||
fetchContactsBasedOnContext(pageNumber.value);
|
fetchContactsBasedOnContext(pageNumber.value);
|
||||||
},
|
},
|
||||||
@ -222,6 +252,13 @@ watch(searchQuery, value => {
|
|||||||
searchValue.value = value || '';
|
searchValue.value = value || '';
|
||||||
// Reset the view if there is search query when we click on the sidebar group
|
// Reset the view if there is search query when we click on the sidebar group
|
||||||
if (value === undefined) {
|
if (value === undefined) {
|
||||||
|
if (
|
||||||
|
isActiveView.value ||
|
||||||
|
activeLabel.value ||
|
||||||
|
activeSegment.value ||
|
||||||
|
hasAppliedFilters.value
|
||||||
|
)
|
||||||
|
return;
|
||||||
fetchContacts();
|
fetchContacts();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -232,6 +269,10 @@ onMounted(async () => {
|
|||||||
await searchContacts(searchQuery.value, pageNumber.value);
|
await searchContacts(searchQuery.value, pageNumber.value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (isActiveView.value) {
|
||||||
|
await fetchActiveContacts(pageNumber.value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await fetchContacts(pageNumber.value);
|
await fetchContacts(pageNumber.value);
|
||||||
} else if (activeSegment.value && activeSegmentId.value) {
|
} else if (activeSegment.value && activeSegmentId.value) {
|
||||||
await fetchSavedOrAppliedFilteredContact(
|
await fetchSavedOrAppliedFilteredContact(
|
||||||
@ -286,11 +327,7 @@ onMounted(async () => {
|
|||||||
class="flex items-center justify-center py-10"
|
class="flex items-center justify-center py-10"
|
||||||
>
|
>
|
||||||
<span class="text-base text-n-slate-11">
|
<span class="text-base text-n-slate-11">
|
||||||
{{
|
{{ emptyStateMessage }}
|
||||||
searchQuery || !hasAppliedFilters
|
|
||||||
? t('CONTACTS_LAYOUT.EMPTY_STATE.SEARCH_EMPTY_STATE_TITLE')
|
|
||||||
: t('CONTACTS_LAYOUT.EMPTY_STATE.LIST_EMPTY_STATE_TITLE')
|
|
||||||
}}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,12 @@ export const routes = [
|
|||||||
component: ContactsIndex,
|
component: ContactsIndex,
|
||||||
meta: commonMeta,
|
meta: commonMeta,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'active',
|
||||||
|
name: 'contacts_dashboard_active',
|
||||||
|
component: ContactsIndex,
|
||||||
|
meta: commonMeta,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -75,6 +75,21 @@ export const actions = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
active: async ({ commit }, { page = 1, sortAttr } = {}) => {
|
||||||
|
commit(types.SET_CONTACT_UI_FLAG, { isFetching: true });
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
data: { payload, meta },
|
||||||
|
} = await ContactAPI.active(page, sortAttr);
|
||||||
|
commit(types.CLEAR_CONTACTS);
|
||||||
|
commit(types.SET_CONTACTS, payload);
|
||||||
|
commit(types.SET_CONTACT_META, meta);
|
||||||
|
commit(types.SET_CONTACT_UI_FLAG, { isFetching: false });
|
||||||
|
} catch (error) {
|
||||||
|
commit(types.SET_CONTACT_UI_FLAG, { isFetching: false });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
show: async ({ commit }, { id }) => {
|
show: async ({ commit }, { id }) => {
|
||||||
commit(types.SET_CONTACT_UI_FLAG, { isFetchingItem: true });
|
commit(types.SET_CONTACT_UI_FLAG, { isFetchingItem: true });
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -70,6 +70,30 @@ describe('#actions', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#active', () => {
|
||||||
|
it('sends correct mutations if API is success', async () => {
|
||||||
|
axios.get.mockResolvedValue({
|
||||||
|
data: { payload: contactList, meta: { count: 100, current_page: 1 } },
|
||||||
|
});
|
||||||
|
await actions.active({ commit });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.SET_CONTACT_UI_FLAG, { isFetching: true }],
|
||||||
|
[types.CLEAR_CONTACTS],
|
||||||
|
[types.SET_CONTACTS, contactList],
|
||||||
|
[types.SET_CONTACT_META, { count: 100, current_page: 1 }],
|
||||||
|
[types.SET_CONTACT_UI_FLAG, { isFetching: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
it('sends correct mutations if API is error', async () => {
|
||||||
|
axios.get.mockRejectedValue({ message: 'Incorrect header' });
|
||||||
|
await actions.active({ commit });
|
||||||
|
expect(commit.mock.calls).toEqual([
|
||||||
|
[types.SET_CONTACT_UI_FLAG, { isFetching: true }],
|
||||||
|
[types.SET_CONTACT_UI_FLAG, { isFetching: false }],
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#update', () => {
|
describe('#update', () => {
|
||||||
it('sends correct mutations if API is success', async () => {
|
it('sends correct mutations if API is success', async () => {
|
||||||
axios.patch.mockResolvedValue({ data: { payload: contactList[0] } });
|
axios.patch.mockResolvedValue({ data: { payload: contactList[0] } });
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user