feat: İhaleist - Akıllı İhale Takip ve Analiz Platformu (Full PRD + 7 Enterprise Modül)

This commit is contained in:
2026-08-07 16:30:24 +03:00
commit ece74feefe
35 changed files with 7820 additions and 0 deletions
+275
View File
@@ -0,0 +1,275 @@
import React, { useState } from 'react';
import { useApp } from '../context/AppContext';
import { INSTITUTION_TYPES_LIST, CITIES_LIST, SECTORS_LIST } from '../data/mockTenders';
import { Bot, ArrowLeft, CheckCircle2, HelpCircle, Building2, MapPin, Tag } from 'lucide-react';
export const SmartFilterBuilder = () => {
const { savedFilters, addSavedFilter, deleteSavedFilter, setActiveTab } = useApp();
const [filterName, setFilterName] = useState('');
const [selectedKurumTip, setSelectedKurumTip] = useState('');
const [selectedUsul, setSelectedUsul] = useState('');
const [selectedCity, setSelectedCity] = useState('');
const [selectedRegion, setSelectedRegion] = useState('');
const [selectedSektor, setSelectedSektor] = useState('');
const [successMessage, setSuccessMessage] = useState('');
const regions = [
'AKDENİZ BÖLGESİ', 'DOĞU ANADOLU BÖLGESİ', 'EGE BÖLGESİ',
'GÜNEYDOĞU ANADOLU BÖLGESİ', 'İÇ ANADOLU BÖLGESİ', 'KARADENİZ BÖLGESİ', 'MARMARA BÖLGESİ'
];
const usuller = [
'Açık İhale', 'Dış Alım', 'Doğrudan Temin', 'İstisna', 'Pazarlık Usulü', 'Yaklaşık Maliyet'
];
const handleCreateFilter = (e) => {
e.preventDefault();
if (!filterName.trim()) {
alert('Lütfen bir filtre adı giriniz.');
return;
}
addSavedFilter({
name: filterName.trim(),
kurumTipi: selectedKurumTip,
usul: selectedUsul,
sehir: selectedCity,
bolge: selectedRegion,
sektor: selectedSektor
});
setSuccessMessage('Yeni akıllı filtre başarıyla oluşturuldu.');
setFilterName('');
setTimeout(() => setSuccessMessage(''), 4000);
};
return (
<div className="max-w-[1600px] mx-auto my-4 space-y-4 animate-fade-in">
{/* Top Banner */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-emerald-600 text-white flex items-center justify-center shadow-md">
<Bot className="w-6 h-6" />
</div>
<div>
<h1 className="text-xl font-black text-slate-900">Akıllı Filtre (Bot) Kurulumu</h1>
<p className="text-xs text-slate-500">Sektör veya şehrinize uygun yeni ihale eklendiğinde anında haberiniz olsun.</p>
</div>
</div>
<button
onClick={() => setActiveTab('list')}
className="bg-slate-800 hover:bg-slate-700 text-white font-semibold text-xs px-4 py-2 rounded-lg flex items-center gap-2 transition-colors shadow-xs"
>
<HelpCircle className="w-4 h-4" />
<span>Nasıl Çalışır?</span>
</button>
</div>
{successMessage && (
<div className="bg-emerald-50 border border-emerald-200 text-emerald-800 px-4 py-3 rounded-xl text-xs font-semibold flex items-center gap-2 animate-fade-in shadow-xs">
<CheckCircle2 className="w-5 h-5 text-emerald-600 shrink-0" />
<span>{successMessage}</span>
</div>
)}
{/* Main Grid */}
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6">
{/* Left Column: Ekip Filtre Listesi */}
<div className="bg-white rounded-xl border border-slate-200 shadow-xs p-4 h-fit">
<div className="flex items-center justify-between mb-4 pb-2 border-b border-slate-100">
<h3 className="font-bold text-xs uppercase tracking-wider text-slate-800 flex items-center gap-2">
<Bot className="w-4 h-4 text-blue-600" /> Ekip Filtre Listesi
</h3>
<span className="bg-blue-100 text-blue-800 text-[10px] font-bold px-2 py-0.5 rounded-full">
{savedFilters.length}
</span>
</div>
<div className="space-y-2 mb-4">
{savedFilters.map(f => (
<div key={f.id} className="p-3 bg-slate-50 border border-slate-200 rounded-lg text-xs hover:bg-slate-100 transition-colors">
<div className="flex items-center justify-between font-bold text-slate-900">
<span>🤖 {f.name}</span>
<button
onClick={() => deleteSavedFilter(f.id)}
className="text-slate-400 hover:text-red-600 text-xs"
>
🗑️
</button>
</div>
<p className="text-[10px] text-slate-500 mt-1">👤 {f.user} {f.sehir && `• 📍 ${f.sehir}`}</p>
</div>
))}
</div>
<button
onClick={() => setActiveTab('list')}
className="w-full bg-slate-100 hover:bg-slate-200 text-slate-700 font-bold py-2.5 px-3 rounded-lg text-xs flex items-center justify-center gap-2 transition-colors border border-slate-200"
>
<ArrowLeft className="w-4 h-4" />
<span>Ana Ekrana Dön</span>
</button>
</div>
{/* Right 3 Columns: Filter Form */}
<div className="lg:col-span-3 space-y-6">
<form onSubmit={handleCreateFilter} className="space-y-6">
{/* Filter Name Card */}
<div className="bg-white rounded-xl border border-slate-200 shadow-xs p-5 space-y-2">
<label className="block text-xs font-bold text-slate-800 uppercase tracking-wider">
FİLTRE ADI (ZORUNLU)
</label>
<input
type="text"
required
value={filterName}
onChange={(e) => setFilterName(e.target.value)}
placeholder="Örn: Ege Bölgesi Cihaz İhaleleri"
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-4 py-2.5 text-sm text-slate-800 placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<p className="text-[11px] text-slate-400">Bu isim sadece sizin takip ve bildirim akışınızda gösterilecektir.</p>
</div>
{/* Middle Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
{/* Kurum Tipi & İhale Usulü */}
<div className="bg-white rounded-xl border border-slate-200 shadow-xs p-5 space-y-4">
<div className="flex items-center gap-2 border-b border-slate-100 pb-3">
<Building2 className="w-5 h-5 text-blue-600" />
<h3 className="font-bold text-sm text-slate-800">Kurum Tipi & İhale Usulü</h3>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 mb-1.5">KURUM TİPLERİ</label>
<div className="flex flex-wrap gap-1.5 mb-2">
{['Sağlık', 'Belediye', 'Üniversite', 'Kamu', 'Temizle'].map(badge => (
<button
key={badge}
type="button"
onClick={() => {
if (badge === 'Temizle') setSelectedKurumTip('');
else if (badge === 'Sağlık') setSelectedKurumTip('Eğitim ve Araştırma Hastaneleri');
else if (badge === 'Belediye') setSelectedKurumTip('Belediyeler');
else if (badge === 'Üniversite') setSelectedKurumTip('Üniversiteler');
else setSelectedKurumTip('Bakanlıklar');
}}
className="bg-red-50 text-red-600 hover:bg-red-100 text-[11px] font-bold px-2.5 py-1 rounded-md border border-red-200 transition-colors"
>
{badge}
</button>
))}
</div>
<select
value={selectedKurumTip}
onChange={(e) => setSelectedKurumTip(e.target.value)}
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2 text-xs text-slate-800"
>
<option value="">Tüm Kurum Tipleri</option>
{INSTITUTION_TYPES_LIST.map(k => (
<option key={k} value={k}>{k}</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 mb-1.5">İHALE USULLERİ</label>
<div className="space-y-1.5 max-h-40 overflow-y-auto pr-1">
{usuller.map(usul => (
<label key={usul} className="flex items-center gap-2 text-xs text-slate-700 cursor-pointer hover:bg-slate-50 p-1 rounded">
<input
type="radio"
name="usul_radio"
checked={selectedUsul === usul}
onChange={() => setSelectedUsul(usul)}
className="accent-blue-600"
/>
<span>{usul}</span>
</label>
))}
</div>
</div>
</div>
{/* Konum Seçimi */}
<div className="bg-white rounded-xl border border-slate-200 shadow-xs p-5 space-y-4">
<div className="flex items-center gap-2 border-b border-slate-100 pb-3">
<MapPin className="w-5 h-5 text-blue-600" />
<h3 className="font-bold text-sm text-slate-800">Konum Seçimi</h3>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 mb-1.5">BÖLGELER</label>
<select
value={selectedRegion}
onChange={(e) => setSelectedRegion(e.target.value)}
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2 text-xs text-slate-800"
>
<option value="">Tüm Bölgeler</option>
{regions.map(r => (
<option key={r} value={r}>{r}</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 mb-1.5">ŞEHİRLER</label>
<select
value={selectedCity}
onChange={(e) => setSelectedCity(e.target.value)}
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2 text-xs text-slate-800"
>
<option value="">Tüm Şehirler</option>
{CITIES_LIST.map(city => (
<option key={city} value={city}>{city}</option>
))}
</select>
</div>
<div>
<label className="block text-xs font-semibold text-slate-600 mb-1.5">SEKTÖR</label>
<select
value={selectedSektor}
onChange={(e) => setSelectedSektor(e.target.value)}
className="w-full bg-slate-50 border border-slate-300 rounded-lg px-3 py-2 text-xs text-slate-800"
>
<option value="">Tüm Sektörler</option>
{SECTORS_LIST.map(s => (
<option key={s} value={s}>{s}</option>
))}
</select>
</div>
</div>
</div>
{/* Action Buttons */}
<div className="flex justify-end gap-3 pt-2">
<button
type="button"
onClick={() => setActiveTab('list')}
className="bg-slate-200 hover:bg-slate-300 text-slate-700 font-semibold px-5 py-2.5 rounded-lg text-xs transition-colors"
>
İptal
</button>
<button
type="submit"
className="bg-blue-600 hover:bg-blue-500 text-white font-bold px-6 py-2.5 rounded-lg text-xs transition-colors shadow-md flex items-center gap-2"
>
<Bot className="w-4 h-4" />
<span>Akıllı Filtre Botunu Kaydet & Çalıştır</span>
</button>
</div>
</form>
</div>
</div>
</div>
);
};