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