Files
ihaleist/src/components/tools/ContractSearch.jsx
T
hikmettanandClaude Fable 5.1 08c0a0af7e Kullanıcı testi hataları: sabit tarihler, kişisel varsayılanlar, kılavuz düğmesi, mobil başlık, AI taslak çökmesi
- Günlük Bülten ve İhale Takvimi sabit "10.08.2026" yerine bugünden hesaplar;
  takvim 7 günü gerçek ihale tarihleriyle sayar
- Ekip/üye/firma varsayılanları oturumdaki kullanıcıdan; sahte "Ekip Notları"
  ve sabit "%94 Uyum" rozeti kaldırıldı (gerçek uyum skoru varsa gösterilir)
- Kılavuz düğmesi document.body'ye portal (ekran dışına düşüyordu)
- Detay modalı havuzdaki güncel kaydı okur: Takip Et / Görev aç etiketleri güncellenir
- Mobil: başlıkta ikincil düğmeler md altında gizli, arama formu küçülebilir
- PWA sayfası: sahte önbellek sayıları yerine Cache API ve bellek sayımı
- Sözleşme sorgula pazar payı bir ondalık (hep %0 görünüyordu)
- Rakip Radar "SMS" vaadi düzeltildi; sektör=CPV etiketi tekrarı gizlendi;
  Stripe kurulum uyarısı yalnız yöneticiye
- Açılışta 7 gereksiz PUT /api/me/data: ilk yükleme yazımı atlanır
- server: teklif taslağı çökmesi (kalemOku nesne döndürür), denetim yanıtından
  model adı kaldırıldı, gereklilikler satır bazlı; derin analiz 12k token +
  kesik JSON onarımı

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-05 09:43:41 +03:00

92 lines
4.4 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { useApp } from '../../context/AppContext';
import { firmStats, formatTL } from '../../services/analytics';
import { FileCheck2, Search, Award } from 'lucide-react';
export const ContractSearch = () => {
const { tenders } = useApp();
const [searchTerm, setSearchTerm] = useState('');
const { firms } = firmStats(tenders);
const filtered = firms.filter((c) =>
c.name.toLocaleLowerCase('tr-TR').includes(searchTerm.toLocaleLowerCase('tr-TR'))
);
return (
<div className="space-y-4 my-4 animate-fade-in">
<div className="bg-white rounded-xl border border-slate-200 p-5 shadow-xs flex flex-col md:flex-row items-center justify-between gap-4">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-indigo-600 text-white flex items-center justify-center shadow-md">
<FileCheck2 className="w-6 h-6" />
</div>
<div>
<h1 className="text-xl font-black text-slate-900">Sözleşme Bedeli & Rakip Analizi</h1>
<p className="text-xs text-slate-500">Kazanan firmaların sözleşme hacmini ve pazar payını canlı veriden sorgulayın.</p>
</div>
</div>
<div className="relative w-full md:w-80">
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
placeholder="Firma adı ile arayın..."
className="w-full bg-slate-50 border border-slate-300 rounded-lg pl-9 pr-4 py-2 text-xs font-semibold"
/>
<Search className="w-4 h-4 text-slate-400 absolute left-3 top-1/2 -translate-y-1/2" />
</div>
</div>
{filtered.length === 0 ? (
<div className="bg-white rounded-xl border border-dashed border-slate-300 p-10 text-center">
<Award className="w-8 h-8 text-slate-300 mx-auto mb-3" />
<p className="text-sm font-bold text-slate-500">Henüz kazanan firma verisi yok</p>
<p className="text-xs text-slate-400 mt-1 max-w-md mx-auto leading-relaxed">
Kazanan firma ve sözleşme bedeli verisi, doğrudan temin sonuçları ve EKAP sonuç ilanları
biriktikçe otomatik olarak burada listelenir.
</p>
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{filtered.map((comp, idx) => (
<div key={idx} className="bg-white rounded-xl border border-slate-200 p-5 shadow-xs hover:shadow-md transition-all space-y-4">
<div className="flex items-start justify-between border-b border-slate-100 pb-3">
<div>
<span className="text-[10px] font-extrabold bg-indigo-100 text-indigo-800 px-2 py-0.5 rounded">
Sıra #{idx + 1}
</span>
<h3 className="font-extrabold text-base text-slate-900 mt-1">{comp.name}</h3>
</div>
<Award className="w-6 h-6 text-indigo-600 shrink-0" />
</div>
<div className="grid grid-cols-2 gap-3 text-xs">
<div className="bg-slate-50 p-3 rounded-lg border border-slate-200">
<span className="text-slate-400 block font-medium">Toplam Sözleşme</span>
<span className="text-sm font-black text-indigo-900 mt-0.5 block">{formatTL(comp.total)}</span>
</div>
<div className="bg-slate-50 p-3 rounded-lg border border-slate-200">
<span className="text-slate-400 block font-medium">Kazandığı İhale</span>
<span className="text-sm font-black text-emerald-700 mt-0.5 block">{comp.wins} İhale</span>
</div>
</div>
<div>
<div className="flex justify-between text-xs font-semibold text-slate-700 mb-1">
<span>Pazar Payı (hacim)</span>
<span className="font-bold text-indigo-700">%{Number(comp.share).toLocaleString('tr-TR', { minimumFractionDigits: 1, maximumFractionDigits: 1 })}</span>
</div>
<div className="w-full bg-slate-100 rounded-full h-2 overflow-hidden">
<div className="bg-indigo-600 h-2 rounded-full" style={{ width: `${Math.min(100, Math.max(comp.share * 4, 2))}%` }}></div>
</div>
</div>
</div>
))}
</div>
)}
</div>
);
};