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). */
|
/** IKN'den taze bir EKAP kaydı bulur (id token anlıktır, saklanamaz). */
|
||||||
export async function findByIkn(ikn) {
|
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');
|
if (!yil || !sayi) throw new Error('Geçersiz İKN');
|
||||||
const r = await searchTenders({ iknYili: Number(yil), iknSayi: Number(sayi), take: 1 });
|
const r = await searchTenders({ iknYili: Number(yil), iknSayi: Number(sayi), take: 1 });
|
||||||
const t = r.list[0];
|
const t = r.list[0];
|
||||||
|
|||||||
+4
-1
@@ -162,7 +162,10 @@ function mapAd(ad) {
|
|||||||
const cityUpper = (ad.addressCityName || '').toLocaleUpperCase('tr-TR');
|
const cityUpper = (ad.addressCityName || '').toLocaleUpperCase('tr-TR');
|
||||||
const sehir = titleCaseTr(ad.addressCityName || '');
|
const sehir = titleCaseTr(ad.addressCityName || '');
|
||||||
const tarih = getFilter(ad, 'İhale ve Teklif Açma Tarihi') || '';
|
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 usul = getFilter(ad, 'İhale Usulü') || 'Belirtilmemiş';
|
||||||
const ihaleTuru = getFilter(ad, 'İhale Türü') || '';
|
const ihaleTuru = getFilter(ad, 'İhale Türü') || '';
|
||||||
const searchBase = `${ad.title || ''} ${ad.advertiserName || ''}`; // yalnızca arama için
|
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 [error, setError] = useState(null);
|
||||||
const [bilgi, setBilgi] = useState(null);
|
const [bilgi, setBilgi] = useState(null);
|
||||||
const [ilerleme, setIlerleme] = useState('');
|
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 [belgeler, setBelgeler] = useState([]);
|
||||||
const [yukleniyor, setYukleniyor] = useState(false);
|
const [yukleniyor, setYukleniyor] = useState(false);
|
||||||
const dosyaRef = useRef(null);
|
const dosyaRef = useRef(null);
|
||||||
@@ -106,13 +111,28 @@ export const TenderDocuments = ({ tender }) => {
|
|||||||
|
|
||||||
const dokumaniAc = async () => {
|
const dokumaniAc = async () => {
|
||||||
setBusy('url'); setError(null);
|
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 {
|
try {
|
||||||
const r = await fetch(`/api/documents/url?ikn=${encodeURIComponent(ikn)}`);
|
const r = await fetch(`/api/documents/url?ikn=${encodeURIComponent(ikn)}`);
|
||||||
const d = await r.json();
|
const d = await r.json();
|
||||||
if (!d.success) throw new Error(d.error);
|
if (!d.success) throw new Error(d.error);
|
||||||
setBilgi({ dokumanSayisi: d.dokumanSayisi });
|
setEkapUrl(d.url);
|
||||||
window.open(d.url, '_blank', 'noopener,noreferrer');
|
setBilgi({ dokumanSayisi: d.dokumanSayisi, ekapSekme: sekme ? 'acildi' : 'engellendi' });
|
||||||
|
if (sekme) sekme.location.href = d.url;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
if (sekme) { try { sekme.close(); } catch { /* yoksay */ } }
|
||||||
setError(e.message);
|
setError(e.message);
|
||||||
} finally { setBusy(null); }
|
} finally { setBusy(null); }
|
||||||
};
|
};
|
||||||
@@ -217,7 +237,8 @@ export const TenderDocuments = ({ tender }) => {
|
|||||||
<>
|
<>
|
||||||
<p className="text-xs text-slate-500 leading-relaxed">
|
<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
|
İ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>
|
</p>
|
||||||
|
|
||||||
<div className="flex gap-2 flex-wrap">
|
<div className="flex gap-2 flex-wrap">
|
||||||
@@ -240,9 +261,24 @@ export const TenderDocuments = ({ tender }) => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</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 && (
|
{bilgi && !bilgi.derin && (
|
||||||
<p className="text-[11px] text-slate-500">
|
<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.karakter != null && <>{bilgi.ilanSayisi} ilan · {bilgi.okasSayisi} kalem · {bilgi.karakter.toLocaleString('tr-TR')} karakter metin analize gönderildi. </>}
|
||||||
{bilgi.yuklendi && (
|
{bilgi.yuklendi && (
|
||||||
<>
|
<>
|
||||||
|
|||||||
Reference in New Issue
Block a user