diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 6d1d46f..19eb400 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -1246,3 +1246,9 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö - Kod incelemesinde, güncel portföy ürününü alan ve kontrolleri sıfırlayan düğmenin yalnız setChecks çağırdığı görüldü. Üst kapsayıcıdaki onChange düğme tıklamasında çalışmadığından kaydedilmiş taslak etiketi değişmeden kalabiliyordu. - PreparationChecks üzerinden gelen bütün kontrol güncellemeleri artık taslağı kaydedilmemiş olarak işaretliyor. Fonksiyon biçimindeki state güncellemeleri korunuyor; başarılı kayıt mevcut mekanizmayla uyarıyı kaldırıyor. - Build ve git diff --check başarılı. Bu küçük durum bildirimi düzeltmesi için tarayıcı kabulü ve tam test paketi yeniden çalıştırılmadı. Build büyük paket uyarısı devam ediyor. Genel plan açık. + +### 2026-09-06 — Kayıtlı filtrelerde arama metinleri + +- Kayıtlı filtre oluşturma ekranına anahtar kelime, kurum adı ve İKN / DT no alanları eklendi. Bu alanlar mevcut revizyon kontrollü saved_filters kaydında saklanıyor. Filtre uygulanırken ana arama ve detaylı arama durumlarına ayrı aktarılıyor; dolu alanlar mevcut liste eşleştirmesinde birlikte uygulanıyor. +- Önceki filtre kayıtları yeni alanlar olmadan çalışmaya devam ediyor ve önceki arama metinlerini temizliyor. Yeni metin alanları okunurken tür doğrulaması ve kenar boşluk temizliği uygulanıyor. +- JSON çevriminden sonra üç bağımsız alanın korunması ve yanlış türdeki alanların reddi için regresyon testi eklendi. 277 test, build ve diff kontrolü başarılı. Yeni formun tarayıcıda kaydet/yenile/uygula kabulü henüz yapılmadı; etkin detaylı aramayı tek düğmeyle kayıt formuna aktarma eklenmedi. Genel plan açık. diff --git a/src/components/SmartFilterBuilder.jsx b/src/components/SmartFilterBuilder.jsx index f17060e..622b31a 100644 --- a/src/components/SmartFilterBuilder.jsx +++ b/src/components/SmartFilterBuilder.jsx @@ -7,6 +7,9 @@ export const SmartFilterBuilder = () => { const { savedFilters, applySavedFilter, addSavedFilter, deleteSavedFilter, setActiveTab, sektorListesi, savingCount, saveError } = useApp(); const [filterName, setFilterName] = useState(''); + const [keyword,setKeyword] = useState(''); + const [institutionName,setInstitutionName] = useState(''); + const [registration,setRegistration] = useState(''); const [selectedKurumTip, setSelectedKurumTip] = useState(''); const [selectedUsul, setSelectedUsul] = useState(''); const [selectedCity, setSelectedCity] = useState(''); @@ -33,6 +36,9 @@ export const SmartFilterBuilder = () => { setSuccessMessage(''); const saved=await addSavedFilter({ name: filterName.trim(), + anahtarKelime: keyword.trim(), + kurumAdi: institutionName.trim(), + ikn: registration.trim(), kurumTipi: selectedKurumTip, usul: selectedUsul, sehir: selectedCity, @@ -139,6 +145,18 @@ export const SmartFilterBuilder = () => {

Bu ad kayıtlı filtre listenizde görünür.

+
+

Arama metinleri

+

Dolu alanlar birlikte uygulanır. Boş bıraktığınız alan aramayı sınırlamaz.

+ {[ + ['Anahtar kelime',keyword,setKeyword], + ['Kurum adı',institutionName,setInstitutionName], + ['İKN / DT no',registration,setRegistration], + ].map(([label,value,setValue])=>)} +
+ {/* Middle Grid */}
diff --git a/src/context/AppContext.jsx b/src/context/AppContext.jsx index c573e04..a27e2ee 100644 --- a/src/context/AppContext.jsx +++ b/src/context/AppContext.jsx @@ -735,8 +735,8 @@ export const AppProvider = ({ children }) => { const applySavedFilter = filter => { try { const criteria=savedFilterCriteria(filter); - setDetailedQueries({institution:'',registration:''}); - setSearchQuery('');setSelectedCpvler([]);setQuickFilter('all'); + setDetailedQueries({institution:criteria.institutionName,registration:criteria.registration}); + setSearchQuery(criteria.keyword);setSelectedCpvler([]);setQuickFilter('all'); setSelectedCity(criteria.city);setSelectedRegion(criteria.region); setSelectedAlimTuru(criteria.procedure);setSelectedKurumTipi(criteria.institution); setSelectedSektorler(criteria.sector?[criteria.sector]:[]);setActiveTab('list');setSaveError(null); diff --git a/src/services/savedFilterCriteria.js b/src/services/savedFilterCriteria.js index 5f80fc1..2fcbeee 100644 --- a/src/services/savedFilterCriteria.js +++ b/src/services/savedFilterCriteria.js @@ -15,5 +15,5 @@ export function savedFilterCriteria(filter){ const read=key=>{if(filter[key]!=null&&typeof filter[key]!=='string')throw new Error('Kayıtlı filtre ölçütü geçersiz.');return (filter[key] || '').trim();}; const region=read('bolge'); if(region&&!Object.keys(REGION_CITIES).includes(norm(region)))throw new Error('Kayıtlı bölge tanınmadı; filtreyi yeniden düzenleyin.'); - return {city:read('sehir'),region:region?norm(region):'',sector:read('sektor'),institution:read('kurumTipi'),procedure:read('usul')}; + return {city:read('sehir'),region:region?norm(region):'',sector:read('sektor'),institution:read('kurumTipi'),procedure:read('usul'),keyword:read('anahtarKelime'),institutionName:read('kurumAdi'),registration:read('ikn')}; } diff --git a/tests/saved-filter-criteria.test.mjs b/tests/saved-filter-criteria.test.mjs index 72da459..37d2e6b 100644 --- a/tests/saved-filter-criteria.test.mjs +++ b/tests/saved-filter-criteria.test.mjs @@ -10,10 +10,17 @@ test('province-based regions cover all 81 cities exactly once and normalize Turk assert.equal(matchesRegion({sehir:''},'KARADENİZ BÖLGESİ'),false); }); test('saved criteria preserves all five fields and rejects unrecognized regions',()=>{ - assert.deepEqual(savedFilterCriteria({sehir:' Giresun ',bolge:'karadeniz bölgesi',sektor:'Laboratuvar',kurumTipi:'Hastaneler',usul:'Açık İhale'}),{city:'Giresun',region:'KARADENİZ BÖLGESİ',sector:'Laboratuvar',institution:'Hastaneler',procedure:'Açık İhale'}); + assert.deepEqual(savedFilterCriteria({sehir:' Giresun ',bolge:'karadeniz bölgesi',sektor:'Laboratuvar',kurumTipi:'Hastaneler',usul:'Açık İhale'}),{city:'Giresun',region:'KARADENİZ BÖLGESİ',sector:'Laboratuvar',institution:'Hastaneler',procedure:'Açık İhale',keyword:'',institutionName:'',registration:''}); assert.throws(()=>savedFilterCriteria({bolge:'Bilinmeyen'}),/tanınmadı/); assert.throws(()=>savedFilterCriteria({sehir:[]}),/geçersiz/); }); +test('saved search text survives serialization independently and malformed values are rejected',()=>{ + const criteria=savedFilterCriteria(JSON.parse(JSON.stringify({anahtarKelime:' test ürünü ',kurumAdi:' Kayseri ',ikn:' 2026/1534665 '}))); + assert.equal(criteria.keyword,'test ürünü'); + assert.equal(criteria.institutionName,'Kayseri'); + assert.equal(criteria.registration,'2026/1534665'); + for(const key of ['anahtarKelime','kurumAdi','ikn'])assert.throws(()=>savedFilterCriteria({[key]:['invalid']}),/geçersiz/); +}); test('procedure filter uses explicit source procedure, not inferred purchase category',()=>{ assert.equal(matchesProcurement({alimTuru:'Mal Alımı',ihaleUsulu:'Açık İhale'},'Açık İhale'),true); assert.equal(matchesProcurement({alimTuru:'Mal Alımı'},'Açık İhale'),false);