diff --git a/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue b/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue index b38f44018..7f7b8392d 100644 --- a/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue +++ b/app/javascript/dashboard/components-next/Contacts/ContactsListLayout.vue @@ -103,6 +103,7 @@ const showPagination = computed(() => { diff --git a/app/javascript/dashboard/components-next/captain/assistant/BulkSelectBar.vue b/app/javascript/dashboard/components-next/captain/assistant/BulkSelectBar.vue index 0bf2007ff..1774a47a0 100644 --- a/app/javascript/dashboard/components-next/captain/assistant/BulkSelectBar.vue +++ b/app/javascript/dashboard/components-next/captain/assistant/BulkSelectBar.vue @@ -1,5 +1,5 @@ @@ -63,7 +80,7 @@ const bulkCheckboxState = computed({ v-if="hasSelected" class="flex items-center gap-3 py-1 ltr:pl-3 rtl:pr-3 ltr:pr-4 rtl:pl-4 rounded-lg bg-n-solid-2 outline outline-1 outline-n-container shadow" > -
+
{{ selectedCountLabel }} -
-
- -
diff --git a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsBulkActionBar.vue b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsBulkActionBar.vue index e56a2dda7..d19f84d99 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsBulkActionBar.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/components/ContactsBulkActionBar.vue @@ -66,8 +66,7 @@ const selectionModel = computed({ return; } - const shouldSelectAll = - newSet.size === props.visibleContactIds.length && newSet.size > 0; + const shouldSelectAll = props.visibleContactIds.every(id => newSet.has(id)); emit('toggleAll', shouldSelectAll); }, }); diff --git a/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue b/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue index 357758e01..b072fa67d 100644 --- a/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue +++ b/app/javascript/dashboard/routes/dashboard/contacts/pages/ContactsIndex.vue @@ -151,7 +151,13 @@ const openBulkDeleteDialog = () => { }; const toggleSelectAll = shouldSelect => { - selectedContactIds.value = shouldSelect ? [...visibleContactIds.value] : []; + const currentSelection = new Set(selectedContactIds.value); + if (shouldSelect) { + visibleContactIds.value.forEach(id => currentSelection.add(id)); + } else { + visibleContactIds.value.forEach(id => currentSelection.delete(id)); + } + selectedContactIds.value = Array.from(currentSelection); }; const toggleContactSelection = ({ id, value }) => { @@ -190,16 +196,28 @@ const getCommonFetchParams = (page = 1) => ({ label: activeLabel.value, }); -const fetchContacts = async (page = 1) => { - clearSelection(); +const fetchContacts = async (page = 1, options = {}) => { + const { clearSelection: shouldClearSelection = true } = options; + if (shouldClearSelection) { + clearSelection(); + } await store.dispatch('contacts/clearContactFilters'); await store.dispatch('contacts/get', getCommonFetchParams(page)); updatePageParam(page); }; -const fetchSavedOrAppliedFilteredContact = async (payload, page = 1) => { +const fetchSavedOrAppliedFilteredContact = async ( + payload, + page = 1, + options = {} +) => { if (!activeSegmentId.value && !hasAppliedFilters.value) return; - clearSelection(); + + const { clearSelection: shouldClearSelection = true } = options; + if (shouldClearSelection) { + clearSelection(); + } + await store.dispatch('contacts/filter', { ...getCommonFetchParams(page), queryPayload: payload, @@ -207,8 +225,12 @@ const fetchSavedOrAppliedFilteredContact = async (payload, page = 1) => { updatePageParam(page); }; -const fetchActiveContacts = async (page = 1) => { - clearSelection(); +const fetchActiveContacts = async (page = 1, options = {}) => { + const { clearSelection: shouldClearSelection = true } = options; + if (shouldClearSelection) { + clearSelection(); + } + await store.dispatch('contacts/clearContactFilters'); await store.dispatch('contacts/active', { page, @@ -217,28 +239,36 @@ const fetchActiveContacts = async (page = 1) => { updatePageParam(page); }; -const searchContacts = debounce(async (value, page = 1, append = false) => { - if (!append) { - clearSelection(); - searchPageNumber.value = 1; - } - await store.dispatch('contacts/clearContactFilters'); - searchValue.value = value; +const searchContacts = debounce( + async (value, page = 1, append = false, options = {}) => { + const { clearSelection: shouldClearSelection = true } = options; - if (!value) { - updatePageParam(page); - await fetchContacts(page); - return; - } + if (!append) { + searchPageNumber.value = 1; - updatePageParam(page, value); - await store.dispatch('contacts/search', { - ...getCommonFetchParams(page), - search: encodeURIComponent(value), - append, - }); - searchPageNumber.value = page; -}, DEBOUNCE_DELAY); + if (shouldClearSelection) { + clearSelection(); + } + } + await store.dispatch('contacts/clearContactFilters'); + searchValue.value = value; + + if (!value) { + updatePageParam(page); + await fetchContacts(page, { clearSelection: false }); + return; + } + + updatePageParam(page, value); + await store.dispatch('contacts/search', { + ...getCommonFetchParams(page), + search: encodeURIComponent(value), + append, + }); + searchPageNumber.value = page; + }, + DEBOUNCE_DELAY +); const loadMoreSearchResults = async () => { if (!hasMore.value || isLoadingMore.value) return; @@ -256,19 +286,26 @@ const loadMoreSearchResults = async () => { isLoadingMore.value = false; }; -const fetchContactsBasedOnContext = async page => { - clearSelection(); +const fetchContactsBasedOnContext = async (page, options = {}) => { + const { clearSelection: shouldClearSelection = true } = options; + if (shouldClearSelection) { + clearSelection(); + } updatePageParam(page, searchValue.value); if (isFetchingList.value) return; if (searchQuery.value) { - await searchContacts(searchQuery.value, page); + await searchContacts(searchQuery.value, page, false, { + clearSelection: shouldClearSelection, + }); return; } // Reset the search value when we change the view searchValue.value = ''; // If we're on the active route, fetch active contacts if (isActiveView.value) { - await fetchActiveContacts(page); + await fetchActiveContacts(page, { + clearSelection: shouldClearSelection, + }); return; } // If there are applied filters or active segment with query @@ -278,13 +315,20 @@ const fetchContactsBasedOnContext = async page => { ) { const queryPayload = activeSegment.value?.query || filterQueryGenerator(appliedFilters.value); - await fetchSavedOrAppliedFilteredContact(queryPayload, page); + await fetchSavedOrAppliedFilteredContact(queryPayload, page, { + clearSelection: shouldClearSelection, + }); return; } // Default case: fetch regular contacts + label - await fetchContacts(page); + await fetchContacts(page, { + clearSelection: shouldClearSelection, + }); }; +const onPageChange = page => + fetchContactsBasedOnContext(page, { clearSelection: false }); + const assignLabels = async labels => { if (!labels.length || !selectedContactIds.value.length) { return; @@ -338,7 +382,9 @@ const handleSort = async ({ sort, order }) => { }); if (searchQuery.value) { - await searchContacts(searchValue.value); + await searchContacts(searchValue.value, pageNumber.value, false, { + clearSelection: false, + }); return; } @@ -360,17 +406,6 @@ const createContact = async contact => { await store.dispatch('contacts/create', contact); }; -watch( - contacts, - newContacts => { - const idsOnPage = newContacts.map(contact => contact.id); - selectedContactIds.value = selectedContactIds.value.filter(id => - idsOnPage.includes(id) - ); - }, - { deep: true } -); - watch(hasSelection, value => { if (!value) { bulkDeleteDialogRef.value?.close?.(); @@ -416,7 +451,9 @@ watch(searchQuery, value => { onMounted(async () => { if (!activeSegmentId.value) { if (searchQuery.value) { - await searchContacts(searchQuery.value, pageNumber.value); + await searchContacts(searchQuery.value, pageNumber.value, false, { + clearSelection: false, + }); return; } if (isActiveView.value) { @@ -452,8 +489,10 @@ onMounted(async () => { :use-infinite-scroll="isSearchView" :has-more="hasMore" :is-loading-more="isLoadingMore" - @update:current-page="fetchContactsBasedOnContext" - @search="searchContacts" + @update:current-page="onPageChange" + @search=" + value => searchContacts(value, 1, false, { clearSelection: false }) + " @update:sort="handleSort" @apply-filter="fetchSavedOrAppliedFilteredContact" @clear-filters="fetchContacts" @@ -485,6 +524,7 @@ onMounted(async () => { :button-label="t('CONTACTS_LAYOUT.EMPTY_STATE.BUTTON_LABEL')" @create="createContact" /> +
{ {{ emptyStateMessage }}
+