Unified search algorithm across all lists; per-section record visibility (contacts, deals, activities, leads, finance, tickets, notes, projects)

This commit is contained in:
2026-10-06 15:04:46 +03:30
parent 43717392b2
commit e08f1452ef
38 changed files with 699 additions and 420 deletions
+16 -13
View File
@@ -1,5 +1,6 @@
<script setup lang="ts">
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
import { matchesSearch } from '@/lib/search';
import { useRouter } from 'vue-router';
import axios from 'axios';
import { api } from '@/lib/api';
@@ -181,9 +182,9 @@ const canPostInActive = computed(() => {
});
const filteredPeers = computed(() => {
const q = peerPickerQuery.value.trim().toLowerCase();
const q = peerPickerQuery.value.trim();
if (!q) return peers.value;
return peers.value.filter((p) => p.fullName.toLowerCase().includes(q));
return peers.value.filter((p) => matchesSearch(q, [p.fullName]));
});
const selectedGroupPeers = computed(() =>
@@ -251,34 +252,32 @@ const sortedRooms = computed(() =>
);
const filteredRooms = computed(() => {
const q = roomListQuery.value.trim().toLowerCase();
const q = roomListQuery.value.trim();
return sortedRooms.value.filter((room) => {
if (roomListTab.value === 'dm' && room.kind !== 'dm') return false;
if (roomListTab.value === 'group' && room.kind !== 'group') return false;
if (roomListTab.value === 'channel' && room.kind !== 'channel') return false;
if (!q) return true;
const title = roomTitle(room).toLowerCase();
const preview = (room.lastMessagePreview || '').toLowerCase();
return title.includes(q) || preview.includes(q);
return matchesSearch(q, [roomTitle(room), room.lastMessagePreview]);
});
});
const mentionCandidates = computed(() => {
const state = mentionState.value;
if (!state) return [] as ConversationPeerDto[];
const q = state.query.trim().toLowerCase();
const q = state.query.trim();
return peers.value
.filter((p) => !q || p.fullName.toLowerCase().includes(q))
.filter((p) => !q || matchesSearch(q, [p.fullName]))
.slice(0, 8);
});
const chatSearchHits = computed(() => {
const q = chatSearchQuery.value.trim().toLowerCase();
const q = chatSearchQuery.value.trim();
if (!q || !chatSearchOpen.value) return [] as ChatMessage[];
return messages.value.filter((m) => {
if (m.deleted) return false;
if (m.type === 'text') return (m.content || '').toLowerCase().includes(q);
return previewLabelForMessage(m).toLowerCase().includes(q);
if (m.type === 'text') return matchesSearch(q, [m.content]);
return matchesSearch(q, [previewLabelForMessage(m)]);
});
});
@@ -764,9 +763,13 @@ async function searchCrmLinks() {
} else {
const { data } = await api.get<PipelineBoardDto>('/pipelines/board');
const deals = (data.stages || []).flatMap((s) => s.deals || []);
const ql = q.toLowerCase();
crmLinkResults.value = deals
.filter((d) => !ql || (d.title || '').toLowerCase().includes(ql) || d.id.includes(q))
.filter(
(d) =>
!q.trim() ||
d.id.includes(q) ||
matchesSearch(q, [d.title, d.person?.fullName, d.company?.name]),
)
.slice(0, 20)
.map((d) => ({ id: d.id, title: d.title || 'معامله' }));
}