Files
ihaleist/src/components/TeklifCetveliKalemleri.jsx
T

118 lines
5.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, { useEffect, useState } from 'react';
import { Package, Loader2, Search } from 'lucide-react';
import { kalemleriGetir } from '../services/kalem';
/**
* Teklif cetveli kalemleri — ortak havuzdan.
*
* Kalemler EKAP API'sinde yok; doküman sayfası CAPTCHA ile korunduğu için
* sunucu otomatik indiremiyor. Bir kullanıcı ihale dokümanını yüklediğinde
* kalemler ayrıştırılıp havuza yazılıyor ve bu liste herkese görünür oluyor.
*/
export const TeklifCetveliKalemleri = ({ikn, knownItemCount = 0}) => <SharedItems key={ikn || 'unknown'} ikn={ikn} knownItemCount={knownItemCount} />;
const SharedItems = ({ikn, knownItemCount}) => {
const [durum, setDurum] = useState({ yukleniyor: true, kalemler: [], kaynak: null });
const [suzgec, setSuzgec] = useState('');
const [retry, setRetry] = useState(0);
useEffect(() => {
if (!ikn) { setDurum({ yukleniyor: false, kalemler: [], kaynak: null }); return; }
let iptal = false;
const controller = new AbortController();
setDurum({yukleniyor:true,kalemler:[],kaynak:null});
kalemleriGetir(ikn, {signal:controller.signal})
.then((d) => { if (!iptal) setDurum({ yukleniyor: false, kalemler: d.kalemler || [], kaynak: d.kaynak }); })
.catch(error => { if (!iptal) setDurum({ yukleniyor:false, kalemler:[], kaynak:null, hata:error.message }); });
return () => { iptal = true; controller.abort(); };
}, [ikn, retry]);
if (durum.yukleniyor) {
return (
<p className="text-xs text-slate-500 flex items-center gap-2">
<Loader2 className="w-3.5 h-3.5 animate-spin" /> Teklif cetveli kalemleri okunuyor…
</p>
);
}
if (durum.hata) return <div role="alert" className="rounded-xl border border-amber-200 bg-amber-50 p-3 text-xs text-amber-900">
<p>Ortak doküman kalemleri yüklenemedi. {durum.hata}</p>
<button type="button" onClick={() => setRetry(n => n + 1)} className="mt-2 underline font-semibold">Kalemleri yeniden yükle</button>
</div>;
if (!durum.kalemler.length) {
return (
<div className="bg-slate-50 border border-slate-200 rounded-xl p-4">
<h3 className="font-bold text-sm text-slate-900 flex items-center gap-2">
<Package className="w-4 h-4 text-indigo-600" /> Teklif Cetveli Kalemleri
</h3>
<p className="text-xs text-slate-600 leading-relaxed mt-1.5">
{knownItemCount > 0
? `İhale kaydında aşağıda gösterilen ${knownItemCount} kalem var. Ortak doküman havuzunda ayrıca çıkarılmış bir cetvel bulunamadı; mevcut kalemleri resmi dokümanla karşılaştırın.`
: 'Ortak doküman havuzunda bu ihale için kalem bulunamadı. Resmi ihale dokümanını yükleyerek kalemleri çıkarmayı deneyebilirsiniz.'}
{' '}İhale dokümanından ortak havuza aktarılan kalemler diğer kullanıcılara da görünür.
</p>
</div>
);
}
const q = suzgec.trim().toLocaleLowerCase('tr-TR');
const gorunen = q
? durum.kalemler.filter((k) => (k.ad || '').toLocaleLowerCase('tr-TR').includes(q))
: durum.kalemler;
return (
<div className="space-y-2">
<div className="flex items-center justify-between gap-3 flex-wrap">
<h3 className="font-bold text-sm text-slate-900 flex items-center gap-2">
<Package className="w-4 h-4 text-indigo-600" />
Ortak Doküman Kalemleri ({durum.kalemler.length})
</h3>
<label className="relative">
<Search className="w-3.5 h-3.5 text-slate-400 absolute left-2.5 top-1/2 -translate-y-1/2" />
<input
value={suzgec}
onChange={(e) => setSuzgec(e.target.value)}
placeholder="Kalem ara…"
className="text-xs pl-8 pr-3 py-1.5 rounded-lg border border-slate-300 w-44 focus:ring-2 focus:ring-indigo-500 outline-none"
/>
</label>
</div>
<div className="border border-slate-200 rounded-xl overflow-hidden shadow-2xs max-h-96 overflow-y-auto">
<table className="w-full text-left text-xs">
<thead className="bg-slate-100 text-slate-700 font-bold border-b border-slate-200 sticky top-0">
<tr>
<th className="p-2.5 w-12">#</th>
<th className="p-2.5">Kalem adı</th>
<th className="p-2.5 text-right w-32">Miktar</th>
</tr>
</thead>
<tbody className="divide-y divide-slate-100">
{gorunen.map((k, i) => (
<tr key={i} className="hover:bg-indigo-50/40">
<td className="p-2.5 font-bold text-slate-400">{k.sira ?? i + 1}</td>
<td className="p-2.5 font-semibold text-slate-800">{k.ad}</td>
<td className="p-2.5 text-right font-bold text-slate-900 whitespace-nowrap">
{k.miktar != null ? k.miktar.toLocaleString('tr-TR') : '—'} {k.birim || ''}
</td>
</tr>
))}
{gorunen.length === 0 && (
<tr><td colSpan={3} className="p-3 text-center text-slate-400">Bu aramayla eşleşen kalem yok.</td></tr>
)}
</tbody>
</table>
</div>
{durum.kaynak?.dosya && (
<p className="text-[10px] text-slate-400">
Kaynak: <strong>{durum.kaynak.dosya}</strong> — bir kullanıcının yüklediği ihale dokümanından
otomatik çıkarıldı. Teklif vermeden önce şartnamenin aslıyla karşılaştırın.
</p>
)}
</div>
);
};