fix: normalize Turkish search and isolate query item matches

This commit is contained in:
2026-09-06 09:22:05 +03:00
parent efec6e434d
commit a34b758430
5 changed files with 48 additions and 15 deletions
+2 -11
View File
@@ -1,3 +1,4 @@
import { matchesTenderSearch } from '../services/tenderSearch';
import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react';
import { useApp } from '../context/AppContext';
import { TenderCard } from './TenderCard';
@@ -43,17 +44,7 @@ export const TenderList = () => {
const filteredTenders = useMemo(() => {
return tenders.filter(tender => {
// Search Query
if (searchQuery) {
const q = searchQuery.toLowerCase();
const matchTitle = (tender.baslik || '').toLowerCase().includes(q);
const matchKurum = (tender.kurum || '').toLowerCase().includes(q);
const matchIkn = (tender.ikn || '').toLowerCase().includes(q);
const matchItems = tender.items && tender.items.some(i => (i.ad || '').toLowerCase().includes(q));
// Kartta taşınan ilk kalemler + sunucudaki tam kalem araması
const matchKalem = tender.kalemler && tender.kalemler.some(a => (a || '').toLowerCase().includes(q));
const matchHavuz = kalemEslesen && tender.ikn && kalemEslesen.has(tender.ikn);
if (!matchTitle && !matchKurum && !matchIkn && !matchItems && !matchKalem && !matchHavuz) return false;
}
if (!matchesTenderSearch(tender,searchQuery,kalemEslesen)) return false;
// Geçmiş ihaleler: varsayılan olarak GİZLİ. Kullanıcı "geçmişi de göster"
// derse (onlyValidOnly=false) hepsi gelir. Tarihi okunamayan kayıt gizlenmez.
+6 -4
View File
@@ -1,3 +1,4 @@
import { normalizeTenderSearch, matchingItemResults } from '../services/tenderSearch';
import React, {createContext, useContext, useState, useEffect, useMemo, useRef } from 'react';
import { parseWebsiteCapabilitiesDynamically } from '../services/webScraperService';
import { havuzuPuanla, profilMetni, profilTerimleri } from '../services/uyum';
@@ -54,19 +55,20 @@ export const AppProvider = ({ children }) => {
// Kalem havuzunda arama: firmanın kendi ürününü ("eldiven", "ortopedik vida")
// hangi ihalelerin teklif cetvelinde geçtiğini bulması için. Kartta yalnızca
// ilk birkaç kalem taşınıyor, tam arama sunucuda yapılıyor.
const [kalemEslesen, setKalemEslesen] = useState(null); // Map(ikn → eşleşen kalemler)
const [kalemSonucu, setKalemSonucu] = useState(null);
const kalemEslesen = matchingItemResults(kalemSonucu, searchQuery);
useEffect(() => {
const q = searchQuery.trim();
if (q.length < 2) { setKalemEslesen(null); return; }
if (q.length < 2) { setKalemSonucu(null); return; }
let iptal = false;
const zaman = setTimeout(() => {
import('../services/kalem')
.then(({ kalemAra }) => kalemAra(q))
.then((sonuclar) => {
if (iptal) return;
setKalemEslesen(sonuclar.length ? new Map(sonuclar.map((s) => [s.ikn, s.eslesen])) : null);
setKalemSonucu({query:normalizeTenderSearch(q),matches:sonuclar.length ? new Map(sonuclar.map((s) => [s.ikn, s.eslesen])) : null});
})
.catch(() => { if (!iptal) setKalemEslesen(null); });
.catch(() => { if (!iptal) setKalemSonucu(null); });
}, 350);
return () => { iptal = true; clearTimeout(zaman); };
}, [searchQuery]);
+15
View File
@@ -0,0 +1,15 @@
export const normalizeTenderSearch = value => String(value ?? '').normalize('NFKD')
.toLocaleLowerCase('tr-TR').replace(/ı/g,'i').replace(/[\u0300-\u036f]/g,'').replace(/\s+/g,' ').trim();
export function matchingItemResults(result, query) {
return result?.query === normalizeTenderSearch(query) ? result.matches : null;
}
export function matchesTenderSearch(tender, query, itemMatches) {
const q=normalizeTenderSearch(query);
if (!q) return true;
const values=[tender.baslik,tender.kurum,tender.ikn,
...(tender.items || []).map(item=>item.ad),
...(tender.kalemler || []).map(item=>typeof item === 'string' ? item : item.ad)];
return values.some(v=>normalizeTenderSearch(v).includes(q)) || Boolean(itemMatches?.has(tender.ikn));
}