Distinguish shared item inventory from tender rows and load errors
This commit is contained in:
@@ -9,19 +9,23 @@ import { kalemleriGetir } from '../services/kalem';
|
||||
* 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 }) => {
|
||||
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;
|
||||
setDurum((d) => ({ ...d, yukleniyor: true }));
|
||||
kalemleriGetir(ikn)
|
||||
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(() => { if (!iptal) setDurum({ yukleniyor: false, kalemler: [], kaynak: null }); });
|
||||
return () => { iptal = true; };
|
||||
}, [ikn]);
|
||||
.catch(error => { if (!iptal) setDurum({ yukleniyor:false, kalemler:[], kaynak:null, hata:error.message }); });
|
||||
return () => { iptal = true; controller.abort(); };
|
||||
}, [ikn, retry]);
|
||||
|
||||
if (durum.yukleniyor) {
|
||||
return (
|
||||
@@ -31,6 +35,11 @@ export const TeklifCetveliKalemleri = ({ ikn }) => {
|
||||
);
|
||||
}
|
||||
|
||||
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">
|
||||
@@ -38,9 +47,11 @@ export const TeklifCetveliKalemleri = ({ ikn }) => {
|
||||
<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">
|
||||
Bu ihalenin kalem listesi henüz havuzda yok. Kalemler EKAP'ın API'sinde yayımlanmıyor;
|
||||
yalnızca ihale dokümanının içinde bulunuyor. Aşağıdan dokümanı yüklerseniz kalemler
|
||||
otomatik çıkarılır ve bu ihale <strong>tüm kullanıcılarda</strong> kalemleriyle listelenir.
|
||||
{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>
|
||||
);
|
||||
@@ -56,7 +67,7 @@ export const TeklifCetveliKalemleri = ({ ikn }) => {
|
||||
<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" />
|
||||
Teklif Cetveli Kalemleri ({durum.kalemler.length})
|
||||
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" />
|
||||
|
||||
@@ -288,7 +288,7 @@ export const TenderDetailModal = () => {
|
||||
)}
|
||||
|
||||
{/* Ortak havuzdan teklif cetveli kalemleri */}
|
||||
<TeklifCetveliKalemleri ikn={tender.ikn} />
|
||||
<TeklifCetveliKalemleri ikn={tender.ikn} knownItemCount={tender.items?.length || 0} />
|
||||
|
||||
{/* Material Table */}
|
||||
{tender.items?.length > 0 && (
|
||||
|
||||
+15
-5
@@ -8,11 +8,21 @@
|
||||
import { getToken } from './auth.js';
|
||||
|
||||
/** Bir ihalenin tüm kalemleri. */
|
||||
export async function kalemleriGetir(ikn) {
|
||||
const r = await fetch(`/api/kalem?ikn=${encodeURIComponent(ikn)}`);
|
||||
const d = await r.json().catch(() => ({}));
|
||||
if (!d.success) throw new Error(d.error || 'kalemler alınamadı');
|
||||
return d;
|
||||
export async function kalemleriGetir(ikn, {signal, timeoutMs = 30000} = {}) {
|
||||
const controller = new AbortController();
|
||||
const cancel = () => controller.abort();
|
||||
if (signal?.aborted) cancel(); else signal?.addEventListener('abort',cancel,{once:true});
|
||||
let timedOut = false;
|
||||
const timer = setTimeout(() => {timedOut=true;controller.abort();},timeoutMs);
|
||||
try {
|
||||
const r = await fetch(`/api/kalem?ikn=${encodeURIComponent(ikn)}`,{signal:controller.signal});
|
||||
const d = await r.json().catch(() => null);
|
||||
if (!r.ok || d?.success !== true || !Array.isArray(d.kalemler)) throw new Error('Kalem listesi alınamadı. Yeniden deneyin.');
|
||||
return d;
|
||||
} catch (error) {
|
||||
if (timedOut) throw new Error('Kalem listesi yüklenirken zaman aşımı oluştu.');
|
||||
throw error;
|
||||
} finally {clearTimeout(timer);signal?.removeEventListener('abort',cancel);}
|
||||
}
|
||||
|
||||
/** Kalem metninde arama — eşleşen İKN'ler. */
|
||||
|
||||
Reference in New Issue
Block a user