fix: EKAP dokümanları açılmıyor — yeni sekme tıklama anında açılıyor, boşluklu İKN'ler düzeltildi
İki ayrı neden vardı:
1) window.open sunucu yanıtından SONRA çağrılıyordu; Safari, PWA ve sıkı ayarlı
Chrome bunu açılır-pencere sayıp sessizce engelliyordu — düğme "çalışmıyor"
görünüyordu. Sekme artık tıklamanın hemen içinde boş açılır, adres gelince
yönlendirilir. Engellenirse gerçek bir bağlantı (a href) gösterilir; bağlantı
tıklaması hiçbir tarayıcıda engellenmez.
2) ilan.gov.tr bazı kayıtlarda İKN'yi başta/sonda boşluk ve kesme boşluğu (nbsp)
ile veriyor (" 2026/1622878"). Bu 50 kayıtta panel "EKAP formatında değil"
deyip düğmeyi hiç göstermiyordu. İKN artık alınırken boşluksuz saklanıyor;
panel ve doküman araması da savunma amaçlı temizliyor.
Ayrıca: "EKAP'ta 0 doküman kayıtlı" yanıltıcı metni kaldırıldı (EKAP arama
kaydı sayıyı vermiyor), EKAP'ın önce güvenlik doğrulaması istediği açıklandı.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ const UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (
|
||||
|
||||
/** IKN'den taze bir EKAP kaydı bulur (id token anlıktır, saklanamaz). */
|
||||
export async function findByIkn(ikn) {
|
||||
const [yil, sayi] = String(ikn || '').split('/');
|
||||
const [yil, sayi] = String(ikn || '').replace(/[\s\u00a0]+/g, '').split('/');
|
||||
if (!yil || !sayi) throw new Error('Geçersiz İKN');
|
||||
const r = await searchTenders({ iknYili: Number(yil), iknSayi: Number(sayi), take: 1 });
|
||||
const t = r.list[0];
|
||||
|
||||
+4
-1
@@ -162,7 +162,10 @@ function mapAd(ad) {
|
||||
const cityUpper = (ad.addressCityName || '').toLocaleUpperCase('tr-TR');
|
||||
const sehir = titleCaseTr(ad.addressCityName || '');
|
||||
const tarih = getFilter(ad, 'İhale ve Teklif Açma Tarihi') || '';
|
||||
const ikn = getFilter(ad, 'İhale Kayıt No') || ad.adNo || '';
|
||||
// İKN'yi boşluksuz sakla: ilan.gov.tr bazı kayıtlarda başa/sona boşluk ve
|
||||
// kesme boşluğu (nbsp) koyuyor; " 2026/1622878" EKAP formatı sayılmıyor ve
|
||||
// doküman/analiz düğmeleri kapanıyordu (havuzda 50 kayıt).
|
||||
const ikn = String(getFilter(ad, 'İhale Kayıt No') || ad.adNo || '').replace(/[\s\u00a0]+/g, '');
|
||||
const usul = getFilter(ad, 'İhale Usulü') || 'Belirtilmemiş';
|
||||
const ihaleTuru = getFilter(ad, 'İhale Türü') || '';
|
||||
const searchBase = `${ad.title || ''} ${ad.advertiserName || ''}`; // yalnızca arama için
|
||||
|
||||
@@ -16,8 +16,13 @@ export const TenderDocuments = ({ tender }) => {
|
||||
const [error, setError] = useState(null);
|
||||
const [bilgi, setBilgi] = useState(null);
|
||||
const [ilerleme, setIlerleme] = useState('');
|
||||
// Son üretilen EKAP doküman adresi — yeni sekme engellenirse kullanıcı
|
||||
// gerçek bir bağlantıya tıklayarak açabilsin (bağlantı tıklaması engellenmez).
|
||||
const [ekapUrl, setEkapUrl] = useState(null);
|
||||
|
||||
const ikn = tender?.ikn;
|
||||
// Eski kayıtlarda İKN boşluklu gelebiliyor (" 2026/1622878"); EKAP tespiti
|
||||
// ve sunucu çağrıları boşluksuz değerle yapılır.
|
||||
const ikn = String(tender?.ikn || '').replace(/[\s\u00a0]+/g, '') || undefined;
|
||||
const [belgeler, setBelgeler] = useState([]);
|
||||
const [yukleniyor, setYukleniyor] = useState(false);
|
||||
const dosyaRef = useRef(null);
|
||||
@@ -106,13 +111,28 @@ export const TenderDocuments = ({ tender }) => {
|
||||
|
||||
const dokumaniAc = async () => {
|
||||
setBusy('url'); setError(null);
|
||||
// Açılır pencere engelleyici: yeni sekme, tıklamanın HEMEN içinde
|
||||
// (await'ten önce) açılmalı. Sunucu yanıtından sonra çağrılan window.open
|
||||
// Safari, PWA ve sıkı ayarlı Chrome'da sessizce engelleniyor — kullanıcı
|
||||
// "düğme çalışmıyor / dokümanlar açılmıyor" görüyordu. Sekme boş açılır,
|
||||
// adres gelince yönlendirilir; engellenirse aşağıda gerçek bağlantı çıkar.
|
||||
let sekme = null;
|
||||
try { sekme = window.open('', '_blank'); } catch { sekme = null; }
|
||||
if (sekme) {
|
||||
try {
|
||||
sekme.opener = null;
|
||||
sekme.document.write('<p style="font-family:sans-serif;padding:24px;color:#334155">EKAP doküman sayfasına yönlendiriliyor…</p>');
|
||||
} catch { /* yazılamazsa da yönlendirme çalışır */ }
|
||||
}
|
||||
try {
|
||||
const r = await fetch(`/api/documents/url?ikn=${encodeURIComponent(ikn)}`);
|
||||
const d = await r.json();
|
||||
if (!d.success) throw new Error(d.error);
|
||||
setBilgi({ dokumanSayisi: d.dokumanSayisi });
|
||||
window.open(d.url, '_blank', 'noopener,noreferrer');
|
||||
setEkapUrl(d.url);
|
||||
setBilgi({ dokumanSayisi: d.dokumanSayisi, ekapSekme: sekme ? 'acildi' : 'engellendi' });
|
||||
if (sekme) sekme.location.href = d.url;
|
||||
} catch (e) {
|
||||
if (sekme) { try { sekme.close(); } catch { /* yoksay */ } }
|
||||
setError(e.message);
|
||||
} finally { setBusy(null); }
|
||||
};
|
||||
@@ -217,7 +237,8 @@ export const TenderDocuments = ({ tender }) => {
|
||||
<>
|
||||
<p className="text-xs text-slate-500 leading-relaxed">
|
||||
İhale dokümanları EKAP üzerinde tutulur. Aşağıdaki bağlantı sizi doğrudan bu ihalenin
|
||||
EKAP doküman sayfasına götürür; ilan metnini ise analiz için buradan çekebilirsiniz.
|
||||
EKAP doküman sayfasına götürür; EKAP önce kısa bir <strong>güvenlik doğrulaması</strong> (resim seçme)
|
||||
ister, ardından şartname ve ekleri listelenir. İlan metnini ise analiz için buradan çekebilirsiniz.
|
||||
</p>
|
||||
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
@@ -240,9 +261,24 @@ export const TenderDocuments = ({ tender }) => {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{ekapUrl && (
|
||||
<div className={`rounded-lg p-2.5 flex gap-2 border ${bilgi?.ekapSekme === 'engellendi' ? 'bg-amber-50 border-amber-200' : 'bg-slate-50 border-slate-200'}`}>
|
||||
<ExternalLink className="w-3.5 h-3.5 text-blue-600 shrink-0 mt-0.5" />
|
||||
<p className="text-[11px] text-slate-700 leading-relaxed">
|
||||
{bilgi?.ekapSekme === 'engellendi'
|
||||
? <>Tarayıcınız yeni sekmeyi engelledi. </>
|
||||
: <>EKAP doküman sayfası yeni sekmede açıldı. Açılmadıysa: </>}
|
||||
<a href={ekapUrl} target="_blank" rel="noopener noreferrer" className="text-blue-600 font-bold hover:underline">
|
||||
EKAP doküman sayfasını aç
|
||||
</a>
|
||||
{' '}· Bağlantı bir süre sonra geçerliliğini yitirebilir; açılmazsa düğmeye yeniden basın.
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{bilgi && !bilgi.derin && (
|
||||
<p className="text-[11px] text-slate-500">
|
||||
{bilgi.dokumanSayisi != null && <>EKAP'ta {bilgi.dokumanSayisi} doküman kayıtlı. </>}
|
||||
{bilgi.dokumanSayisi > 0 && <>EKAP'ta {bilgi.dokumanSayisi} doküman kayıtlı. </>}
|
||||
{bilgi.karakter != null && <>{bilgi.ilanSayisi} ilan · {bilgi.okasSayisi} kalem · {bilgi.karakter.toLocaleString('tr-TR')} karakter metin analize gönderildi. </>}
|
||||
{bilgi.yuklendi && (
|
||||
<>
|
||||
|
||||
Reference in New Issue
Block a user