mirror of
				https://github.com/lingble/chatwoot.git
				synced 2025-10-31 02:57:57 +00:00 
			
		
		
		
	chore: Fix issues with Contact pages (#10544)
This commit is contained in:
		| @@ -50,7 +50,7 @@ const handleBreadcrumbClick = () => { | ||||
|  | ||||
| <template> | ||||
|   <section | ||||
|     class="flex w-full h-full gap-4 overflow-hidden justify-evenly bg-n-background" | ||||
|     class="flex w-full h-full overflow-hidden justify-evenly bg-n-background" | ||||
|   > | ||||
|     <div | ||||
|       class="flex flex-col w-full h-full transition-all duration-300 ltr:2xl:ml-56 rtl:2xl:mr-56" | ||||
|   | ||||
| @@ -93,7 +93,7 @@ const emit = defineEmits([ | ||||
|               " | ||||
|               color="slate" | ||||
|               size="sm" | ||||
|               class="relative" | ||||
|               class="relative w-8" | ||||
|               variant="ghost" | ||||
|               @click="emit('filter')" | ||||
|             > | ||||
| @@ -109,7 +109,6 @@ const emit = defineEmits([ | ||||
|             icon="i-lucide-save" | ||||
|             color="slate" | ||||
|             size="sm" | ||||
|             class="relative" | ||||
|             variant="ghost" | ||||
|             @click="emit('createSegment')" | ||||
|           /> | ||||
| @@ -118,7 +117,6 @@ const emit = defineEmits([ | ||||
|             icon="i-lucide-trash" | ||||
|             color="slate" | ||||
|             size="sm" | ||||
|             class="relative" | ||||
|             variant="ghost" | ||||
|             @click="emit('deleteSegment')" | ||||
|           /> | ||||
|   | ||||
| @@ -1,28 +1,67 @@ | ||||
| <script setup> | ||||
| import { computed } from 'vue'; | ||||
| import { useI18n } from 'vue-i18n'; | ||||
| import { useRoute } from 'vue-router'; | ||||
| import { useMapGetter } from 'dashboard/composables/store'; | ||||
| import { useCamelCase } from 'dashboard/composables/useTransformKeys'; | ||||
|  | ||||
| import ActiveFilterPreview from 'dashboard/components-next/filter/ActiveFilterPreview.vue'; | ||||
|  | ||||
| const props = defineProps({ | ||||
|   activeSegment: { type: Object, default: null }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits(['clearFilters', 'openFilter']); | ||||
|  | ||||
| const { t } = useI18n(); | ||||
| const route = useRoute(); | ||||
|  | ||||
| const appliedFilters = useMapGetter('contacts/getAppliedContactFiltersV4'); | ||||
| const activeSegmentId = computed(() => route.params.segmentId); | ||||
|  | ||||
| const activeSegmentQuery = computed(() => { | ||||
|   const query = props.activeSegment?.query?.payload; | ||||
|   if (!Array.isArray(query)) return []; | ||||
|  | ||||
|   const newFilters = query.map(filter => { | ||||
|     const transformed = useCamelCase(filter); | ||||
|     return { | ||||
|       attributeKey: transformed.attributeKey, | ||||
|       attributeModel: transformed.attributeModel, | ||||
|       customAttributeType: transformed.customAttributeType, | ||||
|       filterOperator: transformed.filterOperator, | ||||
|       queryOperator: transformed.queryOperator ?? 'and', | ||||
|       values: transformed.values, | ||||
|     }; | ||||
|   }); | ||||
|  | ||||
|   return newFilters; | ||||
| }); | ||||
|  | ||||
| const hasActiveSegments = computed( | ||||
|   () => props.activeSegment && activeSegmentId.value !== 0 | ||||
| ); | ||||
|  | ||||
| const activeFilterQueryData = computed(() => { | ||||
|   return hasActiveSegments.value | ||||
|     ? activeSegmentQuery.value | ||||
|     : appliedFilters.value; | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <template> | ||||
|   <ActiveFilterPreview | ||||
|     :applied-filters="appliedFilters" | ||||
|     :applied-filters="activeFilterQueryData" | ||||
|     :max-visible-filters="2" | ||||
|     :more-filters-label=" | ||||
|       t('CONTACTS_LAYOUT.FILTER.ACTIVE_FILTERS.MORE_FILTERS', { | ||||
|         count: appliedFilters.length - 2, | ||||
|         count: activeFilterQueryData.length - 2, | ||||
|       }) | ||||
|     " | ||||
|     :clear-button-label=" | ||||
|       t('CONTACTS_LAYOUT.FILTER.ACTIVE_FILTERS.CLEAR_FILTERS') | ||||
|     " | ||||
|     :show-clear-button="!hasActiveSegments" | ||||
|     class="max-w-[960px] px-6" | ||||
|     @open-filter="emit('openFilter')" | ||||
|     @clear-filters="emit('clearFilters')" | ||||
|   | ||||
| @@ -7,50 +7,18 @@ import ContactsActiveFiltersPreview from 'dashboard/components-next/Contacts/Con | ||||
| import PaginationFooter from 'dashboard/components-next/pagination/PaginationFooter.vue'; | ||||
|  | ||||
| defineProps({ | ||||
|   searchValue: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   headerTitle: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   showPaginationFooter: { | ||||
|     type: Boolean, | ||||
|     default: true, | ||||
|   }, | ||||
|   currentPage: { | ||||
|     type: Number, | ||||
|     default: 1, | ||||
|   }, | ||||
|   totalItems: { | ||||
|     type: Number, | ||||
|     default: 100, | ||||
|   }, | ||||
|   itemsPerPage: { | ||||
|     type: Number, | ||||
|     default: 15, | ||||
|   }, | ||||
|   activeSort: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   activeOrdering: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   activeSegment: { | ||||
|     type: Object, | ||||
|     default: null, | ||||
|   }, | ||||
|   segmentsId: { | ||||
|     type: [String, Number], | ||||
|     default: 0, | ||||
|   }, | ||||
|   hasAppliedFilters: { | ||||
|     type: Boolean, | ||||
|     default: false, | ||||
|   }, | ||||
|   searchValue: { type: String, default: '' }, | ||||
|   headerTitle: { type: String, default: '' }, | ||||
|   showPaginationFooter: { type: Boolean, default: true }, | ||||
|   currentPage: { type: Number, default: 1 }, | ||||
|   totalItems: { type: Number, default: 100 }, | ||||
|   itemsPerPage: { type: Number, default: 15 }, | ||||
|   activeSort: { type: String, default: '' }, | ||||
|   activeOrdering: { type: String, default: '' }, | ||||
|   activeSegment: { type: Object, default: null }, | ||||
|   segmentsId: { type: [String, Number], default: 0 }, | ||||
|   hasAppliedFilters: { type: Boolean, default: false }, | ||||
|   isFetchingList: { type: Boolean, default: false }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits([ | ||||
| @@ -106,7 +74,12 @@ const openFilter = () => { | ||||
|       <main class="flex-1 overflow-y-auto"> | ||||
|         <div class="w-full mx-auto max-w-[960px]"> | ||||
|           <ContactsActiveFiltersPreview | ||||
|             v-if="hasAppliedFilters && isNotSegmentView" | ||||
|             v-if=" | ||||
|               (hasAppliedFilters || !isNotSegmentView) && | ||||
|               !isFetchingList && | ||||
|               !isLabelView | ||||
|             " | ||||
|             :active-segment="activeSegment" | ||||
|             @clear-filters="emit('clearFilters')" | ||||
|             @open-filter="openFilter" | ||||
|           /> | ||||
|   | ||||
| @@ -34,22 +34,19 @@ const contactConversations = computed(() => | ||||
|   > | ||||
|     <Spinner /> | ||||
|   </div> | ||||
|   <div v-else-if="contactConversations.length > 0" class="flex flex-col py-6"> | ||||
|     <div | ||||
|   <div | ||||
|     v-else-if="contactConversations.length > 0" | ||||
|     class="px-6 py-4 divide-y divide-n-strong [&>*:hover]:!border-y-transparent [&>*:hover+*]:!border-t-transparent" | ||||
|   > | ||||
|     <ConversationCard | ||||
|       v-for="conversation in contactConversations" | ||||
|       :key="conversation.id" | ||||
|       class="border-b border-n-strong" | ||||
|     > | ||||
|       <ConversationCard | ||||
|         v-if="conversation" | ||||
|         :key="conversation.id" | ||||
|         :conversation="conversation" | ||||
|         :contact="contactsById(conversation.meta.sender.id)" | ||||
|         :state-inbox="stateInbox(conversation.inboxId)" | ||||
|         :account-labels="accountLabelsValue" | ||||
|         class="px-6 !rounded-none dark:hover:bg-n-alpha-3 hover:bg-n-alpha-1" | ||||
|       /> | ||||
|     </div> | ||||
|       :conversation="conversation" | ||||
|       :contact="contactsById(conversation.meta.sender.id)" | ||||
|       :state-inbox="stateInbox(conversation.inboxId)" | ||||
|       :account-labels="accountLabelsValue" | ||||
|       class="rounded-none hover:rounded-xl hover:bg-n-alpha-1 dark:hover:bg-n-alpha-3" | ||||
|     /> | ||||
|   </div> | ||||
|   <p v-else class="px-6 py-10 text-sm leading-6 text-center text-n-slate-11"> | ||||
|     {{ t('CONTACTS_LAYOUT.SIDEBAR.HISTORY.EMPTY_STATE') }} | ||||
|   | ||||
| @@ -28,7 +28,7 @@ const handleDelete = () => { | ||||
|  | ||||
| <template> | ||||
|   <div | ||||
|     class="flex flex-col gap-2 px-6 py-2 border-b border-n-strong group/note" | ||||
|     class="flex flex-col gap-2 py-2 mx-6 border-b border-n-strong group/note" | ||||
|   > | ||||
|     <div class="flex items-center justify-between"> | ||||
|       <div class="flex items-center gap-1.5 py-2.5 min-w-0"> | ||||
|   | ||||
| @@ -82,7 +82,7 @@ const onCardClick = e => { | ||||
|  | ||||
| <template> | ||||
|   <div | ||||
|     class="flex w-full gap-3 px-3 py-4 transition-colors duration-300 ease-in-out cursor-pointer rounded-xl" | ||||
|     class="flex w-full gap-3 px-3 py-4 transition-all duration-300 ease-in-out cursor-pointer" | ||||
|     @click="onCardClick" | ||||
|   > | ||||
|     <Avatar | ||||
|   | ||||
| @@ -4,22 +4,11 @@ import { replaceUnderscoreWithSpace } from './helper/filterHelper.js'; | ||||
| import Button from 'dashboard/components-next/button/Button.vue'; | ||||
|  | ||||
| defineProps({ | ||||
|   appliedFilters: { | ||||
|     type: Array, | ||||
|     default: () => [], | ||||
|   }, | ||||
|   maxVisibleFilters: { | ||||
|     type: Number, | ||||
|     default: 2, | ||||
|   }, | ||||
|   clearButtonLabel: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   moreFiltersLabel: { | ||||
|     type: String, | ||||
|     default: '', | ||||
|   }, | ||||
|   appliedFilters: { type: Array, default: () => [] }, | ||||
|   maxVisibleFilters: { type: Number, default: 2 }, | ||||
|   clearButtonLabel: { type: String, default: '' }, | ||||
|   moreFiltersLabel: { type: String, default: '' }, | ||||
|   showClearButton: { type: Boolean, default: true }, | ||||
| }); | ||||
|  | ||||
| const emit = defineEmits(['clearFilters', 'openFilter']); | ||||
| @@ -46,6 +35,9 @@ const formatOperatorLabel = operator => { | ||||
|  | ||||
| const formatFilterValue = value => { | ||||
|   if (!value) return ''; | ||||
|   if (Array.isArray(value)) { | ||||
|     return value.join(', '); | ||||
|   } | ||||
|   if (typeof value === 'object' && value.name) { | ||||
|     return value.name; | ||||
|   } | ||||
| @@ -55,10 +47,7 @@ const formatFilterValue = value => { | ||||
|  | ||||
| <template> | ||||
|   <div class="flex flex-wrap items-center w-full gap-2 mx-auto"> | ||||
|     <template | ||||
|       v-for="(filter, index) in appliedFilters" | ||||
|       :key="filter.attributeKey" | ||||
|     > | ||||
|     <template v-for="(filter, index) in appliedFilters" :key="index"> | ||||
|       <div | ||||
|         v-if="index < maxVisibleFilters" | ||||
|         class="inline-flex items-center gap-2 h-7" | ||||
| @@ -107,8 +96,9 @@ const formatFilterValue = value => { | ||||
|     > | ||||
|       {{ moreFiltersLabel }} | ||||
|     </div> | ||||
|     <div class="w-px h-3 rounded-lg bg-n-strong" /> | ||||
|     <div v-if="showClearButton" class="w-px h-3 rounded-lg bg-n-strong" /> | ||||
|     <Button | ||||
|       v-if="showClearButton" | ||||
|       :label="clearButtonLabel" | ||||
|       size="xs" | ||||
|       class="!px-1" | ||||
|   | ||||
| @@ -256,6 +256,7 @@ onMounted(async () => { | ||||
|       :active-ordering="sortState.activeOrdering" | ||||
|       :active-segment="activeSegment" | ||||
|       :segments-id="activeSegmentId" | ||||
|       :is-fetching-list="isFetchingList" | ||||
|       :has-applied-filters="hasAppliedFilters" | ||||
|       @update:current-page="fetchContactsBasedOnContext" | ||||
|       @search="searchContacts" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Sivin Varghese
					Sivin Varghese