import React, { useState, useEffect, useRef, useId } from 'react'; import { useApp } from '../context/AppContext'; import { BidPackageBuilder } from './BidPackageBuilder'; import { TenderDocuments } from './TenderDocuments'; import { TeklifAsistani } from './TeklifAsistani'; import { TeklifCetveliKalemleri } from './TeklifCetveliKalemleri'; import { X, Star, Share2, Calendar, MapPin, Building2, Tag, FileText, CheckSquare, DollarSign, Calculator, Download, Sparkles, AlertTriangle, ShieldCheck, Send, Stamp, ArrowLeft, ExternalLink, Printer, PenLine } from 'lucide-react'; import { acknowledgeNoteDraft, editNoteDraft } from '../services/noteDrafts'; import { tenderStateKey } from '../services/tenderState'; import { createTenderICS } from '../services/tenderCalendar'; export const TenderDetailModal = () => { const { activeTenderDetail, setActiveTenderDetail, toggleTrackTender, userProfile, setIsAiSpecAnalyzerOpen, gorevAc, gorevKontrol, aiAnalysisResults, tenders, mutateTender, savingCount, saveError, authUser } = useApp(); const dialogRef = useRef(null); const dialogTitleId = useId(); const isOpen = Boolean(activeTenderDetail); useEffect(() => { if (!isOpen) return; const opener = document.activeElement; const root = dialogRef.current; root?.querySelector('button')?.focus(); return () => { if (opener?.isConnected) opener.focus(); }; }, [isOpen]); const handleDialogKey = e => { if (e.defaultPrevented || e.target.closest('[role="dialog"]') !== dialogRef.current) return; if (e.key === 'Escape') { e.preventDefault(); e.stopPropagation(); setActiveTenderDetail(null); return; } if (e.key !== 'Tab') return; const focusable = [...dialogRef.current.querySelectorAll('button, a[href], input, select, textarea, [tabindex]')] .filter(el => el.tabIndex >= 0 && !el.disabled && el.getClientRects().length && !el.closest('[inert]')); const first = focusable[0], last = focusable.at(-1); if (!first) { e.preventDefault(); dialogRef.current.focus(); } else if (e.shiftKey && (document.activeElement === first || document.activeElement === dialogRef.current)) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } }; const [activeTab, setActiveTab] = useState('details'); // 'details', 'pdf_stamp', 'ai_assistant', 'checklist', 'kurum_tenders' const noteOwner = authUser?.email || ''; const [noteDrafts, setNoteDrafts] = useState(() => ({ owner: noteOwner, drafts: {} })); // Discard private drafts on account changes, before rendering another account. if (noteDrafts.owner !== noteOwner) setNoteDrafts({ owner: noteOwner, drafts: {} }); const noteKey = tenderStateKey(activeTenderDetail); const noteDraft = noteDrafts.owner === noteOwner ? noteDrafts.drafts[noteKey] : undefined; const noteText = noteDraft?.text || ''; const setNoteText = text => setNoteDrafts(state => editNoteDraft(state, noteOwner, noteKey, text)); const [actionError, setActionError] = useState(null); // Kur Farkı Calculator State const [bidAmount, setBidAmount] = useState(15000); const [fx, setFx] = useState(null); const [fxError, setFxError] = useState(null); useEffect(() => { if (!activeTenderDetail) return; let cancelled = false; fetch('/api/exchange-rates').then(r => r.json()).then(d => { if (cancelled) return; if (!d.success) throw new Error(d.error); setFx(d); setFxError(d.refreshError || null); }).catch(e => { if (!cancelled) setFxError(e.message); }); return () => { cancelled = true; }; }, [activeTenderDetail?.id]); const usdRate = fx?.rates?.USD; const eurRate = fx?.rates?.EUR; if (!activeTenderDetail) return null; // Havuzdaki güncel kayıt: takip/görev değişince düğme etiketleri de değişsin. const tender = (activeTenderDetail && tenders.find((t) => t.id === activeTenderDetail.id)) || activeTenderDetail; const savedNotes = tender.notes || []; const handleAddNote = async (e) => { e.preventDefault(); if (!noteText.trim()) return; const saved = await mutateTender(tender.id, {notes: [...savedNotes, { id: Date.now(), author: userProfile.name, text: noteText.trim(), date: new Date().toLocaleString('tr-TR') }]}); if (saved) setNoteDrafts(state => acknowledgeNoteDraft(state, noteOwner, noteKey, noteDraft)); }; const handleExportICS = () => { let icsData; try { icsData = createTenderICS(tender); setActionError(null); } catch (e) { setActionError(e.message); return; } const blob = new Blob([icsData], { type: 'text/calendar;charset=utf-8' }); const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.setAttribute('download', `ihale_${tender.ikn}.ics`); document.body.appendChild(link); link.click(); document.body.removeChild(link); setTimeout(() => URL.revokeObjectURL(link.href), 1000); }; return (
{(actionError || saveError) &&

{actionError || saveError}

} {savingCount > 0 &&

Kaydediliyor…

} {/* Top Header Controls */}
{/* İhaleden göreve: teslim tarihi ihale saati; sonuç yayımlanınca bildirim */} {tender.gorev ? ( ✓ Görev açık{tender.gorev.teslim ? ` · ${tender.gorev.teslim}` : ''} ) : ( )}
{/* Scrollable Container */}
{/* Main Title Header */}
{tender.alimTuru} İKN: {tender.ikn} {tender.aciliyetBadge && ( {tender.aciliyetBadge} )} Ekleme: {tender.eklenmeZamani}

{tender.baslik}

{/* Metadata Cards Grid */}
Kurum Adı {tender.kurum}
İhale Tarihi & Saat {tender.tarih} - {tender.saat}
Şehir / Bölge {tender.sehir} ({tender.bolge})
Yaklaşık Maliyet {tender.yaklasikMaliyet ? `${tender.yaklasikMaliyet.toLocaleString('tr-TR')} TL` : 'Gizli / Belirtilmemiş'}
{/* Navigation Tabs */}
{[ { id: 'details', label: 'İhale Detayları', icon: FileText }, { id: 'pdf_stamp', label: 'Birim Fiyat Cetveli (PDF & Kaşe)', icon: Stamp, badge: 'İnteraktif' }, { id: 'ai_assistant', label: 'Yapay Zeka Asistanı', icon: Sparkles, badge: tender?.aiMatchScore != null ? `%${tender.aiMatchScore} Uyum` : undefined }, { id: 'checklist', label: 'Hazırlık Checklist', icon: CheckSquare }, { id: 'teklif_asistani', label: 'Teklif Asistanı', icon: PenLine, badge: 'AI' }, ].map(tab => { const Icon = tab.icon; const isActive = activeTab === tab.id; return ( ); })}
{/* Tab 1: İhale Detayları */} {activeTab === 'details' && (
{/* Description & Terms */}

İhale Açıklaması & Teslimat Koşulları

{tender.aciklama}

Teslimat Süresi {tender.teslimatSuresi}
Teslimat Yeri {tender.teslimYeri}
{tender.gorev?.kontrolListesi?.length > 0 && (

Hazırlık görevi — belge kontrol listesi

    {tender.gorev.kontrolListesi.map((k, i) => (
  • ))}
)} {/* Ortak havuzdan teklif cetveli kalemleri */} {/* Material Table */} {tender.items?.length > 0 && (

Malzeme / Kalem Listesi ({tender.items?.length || 0} Kalem)

{tender.items?.map((item, index) => ( ))}
# Malzeme Adı Miktar Tahmini Birim Fiyat Toplam Tutarlar
{index + 1} {item.ad} {item.miktar} {item.birim} {item.birimFiyat ? `${item.birimFiyat} TL` : '—'} {item.birimFiyat ? `${(item.birimFiyat * parseFloat(item.miktar)).toLocaleString('tr-TR')} TL` : '—'}
)} {/* Team Notes Area */}

Ekip Notları

{savedNotes.map(n => (
{n.author} {n.date}

{n.text}

))}
setNoteText(e.target.value)} aria-label="Ekip notu" placeholder="Ekip arkadaşlarınıza not yazın..." className="flex-1 bg-slate-50 border border-slate-300 rounded-lg px-3 py-2 text-xs" />
{/* Right Sidebar of Details: TCMB Kur & Calendar & Specs */}
{/* TCMB Kur Farkı Calculator */}
Kur Farkı Hesaplayıcı TCMB döviz satış
{fx &&

TCMB bülteni: {fx.bulletinDate} · Alındı: {new Date(fx.fetchedAt).toLocaleString('tr-TR')}

} {fxError &&

{fxError}

}
USD / TRY {usdRate ? `₺${usdRate}` : '—'}
EUR / TRY {eurRate ? `₺${eurRate}` : '—'}
setBidAmount(Number(e.target.value))} className="w-full bg-white/10 border border-white/20 rounded-lg px-3 py-1.5 text-xs text-white" />
Dolar Karşılığı: {usdRate ? '$' + (bidAmount / usdRate).toFixed(2) : 'Kur bekleniyor'}
Euro Karşılığı: {eurRate ? '€' + (bidAmount / eurRate).toFixed(2) : 'Kur bekleniyor'}
{/* Downloads & Calendar */}

İhale Dosyaları & Takvim

)} {/* Tab 2: PDF Birim Fiyat Cetveli & Kaşe Editörü */} {activeTab === 'pdf_stamp' && ( )} {/* Tab 3: AI Asistanı */} {activeTab === 'ai_assistant' && (

Yapay Zeka Teklif & Risk Değerlendirmesi

Analizi başlatarak işlenen kaynağı ve kontrol gerektiren alanları inceleyin.

{tender.aiMatchScore != null && ( %{tender.aiMatchScore} Şirket Uyumu )}
{/* Skorun nedeni: hangi profil terimi ihalenin neresinde geçiyor */} {tender.uyumNedenleri?.length > 0 && (
{tender.uyumNedenleri.map((n, i) => ( {n.terim} · {n.alan} ))}
)}
Risk Skoru {tender.aiRiskScore || 'Analiz Bekliyor'}{tender.aiRiskScore ? ' Risk' : ''}
Önerilen Teklif Aralığı {tender.aiBidRange?.recommended != null ? `₺${tender.aiBidRange.recommended.toLocaleString('tr-TR')}` : '—'}
{tender.gecerliTeklifSayisi != null || tender.teklifSayisi != null ? 'Katılan Firma' : 'Rekabet Verisi'} {tender.gecerliTeklifSayisi != null ? `${tender.gecerliTeklifSayisi} geçerli teklif` : tender.teklifSayisi != null ? `${tender.teklifSayisi} teklif` : 'Sonuçlanınca gelir'}

Kritik Yeterlik Şartları

    {tender.yeterlikKriterleri?.map((k, i) => (
  • {k}
  • ))}
)} {/* Tab 5: Teklif Asistanı — belgeye sor / taslak denetimi / taslak üretimi */} {activeTab === 'teklif_asistani' && } {/* Tab 4: Readiness Checklist */} {activeTab === 'checklist' && (

İhale Hazırlık Adımları

{[ 'Birim fiyat cetveli kontrol edildi ve dolduruldu', 'İmza sirküleri ve yetki belgesi hazırlandı', 'ÜTS kayıtları ve SUT kodları doğrulandı', 'TCMB döviz kur riski hesaplandı', 'EKAP üzerinden teklif zarfı oluşturuldu' ].map((item, idx) => ( ))}
)}
); };