fix: combine detailed tender search fields

This commit is contained in:
2026-09-06 15:59:01 +03:00
parent 9b751296ed
commit 3a5b06cee2
8 changed files with 78 additions and 35 deletions
+41 -29
View File
@@ -5,37 +5,34 @@ import { CITIES_LIST, INSTITUTION_TYPES_LIST } from '../data/constants';
import { SlidersHorizontal, Search, RotateCcw, X } from 'lucide-react';
export const DetailedSearchModal = () => {
const {
isDetailedSearchOpen,
setIsDetailedSearchOpen,
searchQuery,
setSearchQuery,
selectedCity,
setSelectedCity,
selectedAlimTuru,
setSelectedAlimTuru,
selectedSektorler,
setSelectedSektorler,
toggleSektor,
sektorListesi,
selectedKurumTipi,
setSelectedKurumTipi,
onlyValidOnly,
setOnlyValidOnly,
setActiveTab
} = useApp();
const {isDetailedSearchOpen}=useApp();
return isDetailedSearchOpen?<DetailedSearchForm/>:null;
};
const DetailedSearchForm = () => {
const context=useApp();
const {setIsDetailedSearchOpen,setActiveTab,sektorListesi}=context;
const titleId=useId();
const modalFocus=useModalFocus(isDetailedSearchOpen,()=>setIsDetailedSearchOpen(false));
const [iknQuery, setIknQuery] = useState('');
const [kurumQuery, setKurumQuery] = useState('');
if (!isDetailedSearchOpen) return null;
const modalFocus=useModalFocus(true,()=>setIsDetailedSearchOpen(false));
const [searchQuery,setSearchQuery]=useState(context.searchQuery);
const [selectedCity,setSelectedCity]=useState(context.selectedCity);
const [selectedAlimTuru,setSelectedAlimTuru]=useState(context.selectedAlimTuru);
const [selectedKurumTipi,setSelectedKurumTipi]=useState(context.selectedKurumTipi);
const [selectedSektorler,setSelectedSektorler]=useState(context.selectedSektorler);
const [onlyValidOnly,setOnlyValidOnly]=useState(context.onlyValidOnly);
const [iknQuery,setIknQuery]=useState(context.detailedQueries.registration);
const [kurumQuery,setKurumQuery]=useState(context.detailedQueries.institution);
const toggleSektor=sector=>setSelectedSektorler(old=>old.includes(sector)?old.filter(value=>value!==sector):[...old,sector]);
const handleApplySearch = (e) => {
e.preventDefault();
if (kurumQuery) setSearchQuery(kurumQuery);
else if (iknQuery) setSearchQuery(iknQuery);
context.setSearchQuery(searchQuery.trim());
context.setDetailedQueries({institution:kurumQuery.trim(),registration:iknQuery.trim()});
context.setSelectedCity(selectedCity);
context.setSelectedAlimTuru(selectedAlimTuru);
context.setSelectedKurumTipi(selectedKurumTipi);
context.setSelectedSektorler(selectedSektorler);
context.setOnlyValidOnly(onlyValidOnly);
setActiveTab('list');
setIsDetailedSearchOpen(false);
@@ -45,8 +42,9 @@ export const DetailedSearchModal = () => {
setSearchQuery('');
setSelectedCity('');
setSelectedAlimTuru('');
setSelectedSektorler([]);
setSelectedKurumTipi('');
setSelectedSektorler([]);
setOnlyValidOnly(true);
setIknQuery('');
setKurumQuery('');
};
@@ -72,6 +70,7 @@ export const DetailedSearchModal = () => {
{/* Form Body */}
<form onSubmit={handleApplySearch} className="p-6 space-y-4 text-xs">
<p className="text-slate-600">Doldurduğunuz alanlar birlikte uygulanır. Değişiklikler İhaleleri Ara düğmesine bastığınızda listeye yansır.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
@@ -82,7 +81,7 @@ export const DetailedSearchModal = () => {
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Başlık, açıklama içinde aranır..."
placeholder="Başlık, kurum, İKN veya kalemde aranır..."
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2 text-slate-800 focus:ring-2 focus:ring-blue-500"
/>
</div>
@@ -168,6 +167,19 @@ export const DetailedSearchModal = () => {
</select>
</div>
<div>
<label className="block font-bold text-slate-700 mb-1">Kurum Tipi</label>
<select
aria-label="Kurum Tipi"
value={selectedKurumTipi}
onChange={(e) => setSelectedKurumTipi(e.target.value)}
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2 text-slate-800"
>
<option value="">Tümü</option>
{INSTITUTION_TYPES_LIST.map(type => <option key={type} value={type}>{type}</option>)}
</select>
</div>
</div>
<div className="pt-2">
+1
View File
@@ -67,6 +67,7 @@ export const Header = () => {
{searchQuery && (
<button
type="button"
aria-label="Anahtar kelime aramasını temizle"
onClick={() => setSearchQuery('')}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-white text-xs"
>
+2 -2
View File
@@ -31,7 +31,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
setSelectedKurumTipi,
selectedCity,
setSelectedCity,
clearAllFilters
clearAllFilters,detailedQueries
} = useApp();
const [sektorAra, setSektorAra] = useState('');
@@ -89,7 +89,7 @@ export const LeftSidebar = ({onOpenProductSearch} = {}) => {
<span className="font-bold text-xs uppercase tracking-wider text-slate-700 flex items-center gap-1.5">
<Compass className="w-4 h-4 text-blue-600" /> Hızlı Erişim
</span>
{(selectedRegion || quickFilter !== 'all' || selectedSektorler.length || selectedCpvler.length || selectedCity || selectedAlimTuru || selectedKurumTipi) && (
{(detailedQueries.institution || detailedQueries.registration || selectedRegion || quickFilter !== 'all' || selectedSektorler.length || selectedCpvler.length || selectedCity || selectedAlimTuru || selectedKurumTipi) && (
<button
onClick={clearAllFilters}
className="text-[11px] font-medium text-blue-600 hover:underline"
+9 -3
View File
@@ -1,5 +1,5 @@
import {matchesRegion,matchesProcurement} from '../services/savedFilterCriteria';
import { matchesTenderSearch } from '../services/tenderSearch';
import { matchesTenderSearch, matchesDetailedSearch } from '../services/tenderSearch';
import React, { useState, useMemo, useEffect, useRef, useCallback } from 'react';
import { useApp } from '../context/AppContext';
import { TenderCard } from './TenderCard';
@@ -17,6 +17,7 @@ export const TenderList = () => {
const {
tenders,
searchQuery,
detailedQueries,setDetailedQueries,
kalemEslesen, itemSearchState, retryItemSearch,
uyumEsigi,
selectedCity,selectedRegion,setSelectedRegion,
@@ -46,6 +47,7 @@ export const TenderList = () => {
return tenders.filter(tender => {
// Search Query
if (!matchesTenderSearch(tender,searchQuery,kalemEslesen)) return false;
if (!matchesDetailedSearch(tender,detailedQueries)) 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.
@@ -71,7 +73,7 @@ export const TenderList = () => {
if (sortBy === 'distant') return compareTenderDeadlines(a,b,-1);
return 0;
});
}, [tenders, searchQuery, kalemEslesen, uyumEsigi, selectedCity, selectedRegion, selectedAlimTuru, selectedSektorler, selectedCpvler, selectedKurumTipi, quickFilter, sortBy, onlyValidOnly, now, favoriteKurumlar, oncekiGiris]);
}, [tenders, searchQuery, detailedQueries, kalemEslesen, uyumEsigi, selectedCity, selectedRegion, selectedAlimTuru, selectedSektorler, selectedCpvler, selectedKurumTipi, quickFilter, sortBy, onlyValidOnly, now, favoriteKurumlar, oncekiGiris]);
const gorunenTenders = filteredTenders.slice(0, gosterilen);
const dahaVar = gosterilen < filteredTenders.length;
@@ -80,7 +82,7 @@ export const TenderList = () => {
// dizisine bağlansaydı havuz her yenilendiğinde (10 dk) kullanıcı listenin
// başına fırlardı.
useEffect(() => { setGosterilen(SAYFA_ADIM); }, [
searchQuery, selectedCity, selectedRegion, selectedAlimTuru, selectedSektorler,
searchQuery, detailedQueries, selectedCity, selectedRegion, selectedAlimTuru, selectedSektorler,
selectedCpvler, selectedKurumTipi, quickFilter, sortBy, onlyValidOnly,
]);
@@ -129,6 +131,10 @@ export const TenderList = () => {
return (
<div className="w-full min-w-0 flex-1 space-y-4">
{Object.entries(detailedQueries).filter(([,value])=>value).map(([key,value])=><div key={key} className="flex gap-3 items-center flex-wrap text-sm bg-blue-50 border rounded p-3">
<span>{key==='institution'?'Kurum adı':'İKN / DT no'}: {value}</span>
<button className="underline text-blue-800" onClick={()=>setDetailedQueries(old=>({...old,[key]:''}))}>{key==='institution'?'Kurum adı filtresini kaldır':'İKN filtresini kaldır'}</button>
</div>)}
{selectedRegion&&<div className="flex gap-3 items-center flex-wrap text-sm bg-blue-50 border rounded p-3"><span>İl bazında bölge filtresi: {selectedRegion}</span><button className="underline text-blue-800" onClick={()=>setSelectedRegion('')}>Bölge filtresini kaldır</button></div>}
{/* Header bar above list */}