import { TenderSourceHistory } from './TenderSourceHistory';
import React, { useState, useEffect, useId } from 'react';
import {useModalFocus} from '../hooks/useModalFocus';
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, detailLoad, retryTenderDetail } = useApp();
const dialogTitleId = useId();
const isOpen = Boolean(activeTenderDetail);
const {ref:dialogRef,onKeyDown:handleDialogKey}=useModalFocus(isOpen,()=>setActiveTenderDetail(null));
const [activeTab, setActiveTab] = useState('details'); // 'details', 'pdf_stamp', 'ai_assistant', 'checklist', 'kurum_tenders'
useEffect(()=>{setActiveTab(activeTenderDetail?.preparationTarget ? 'pdf_stamp' : 'details');},[activeTenderDetail?.id,activeTenderDetail?.preparationTarget]);
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;
const fxIsStale = Boolean(fx?.refreshError);
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}
}
{tender.ikn &&
}
{tender.sourceChanges?.length > 0 &&
Son kaydettiğiniz bilgilere göre ihale değişmiş
{tender.sourceChanges.map(change => {change.label}: {change.before} → {change.after} )}
Takip, not ve hazırlık kayıtlarınız korundu. Yeni koşulları resmi kaynakla karşılaştırın.
}
{tender.sourceWarning &&
{tender.sourceWarning}
}
{detailLoad?.id === tender.id && detailLoad.status === 'loading' &&
Kaynak ilan metni yükleniyor…
}
{detailLoad?.id === tender.id && detailLoad.status === 'error' &&
{detailLoad.error}
İlan metnini yeniden yükle
}
{savingCount > 0 &&
Kaydediliyor…
}
{/* Top Header Controls */}
setActiveTenderDetail(null)}
className="flex items-center gap-2 text-xs font-semibold text-slate-300 hover:text-white transition-colors bg-slate-800 px-3 py-1.5 rounded-lg border border-slate-700"
>
Listeye Geri Dön
0} aria-pressed={Boolean(tender.incelendi)}
onClick={() => mutateTender(tender.id,{incelendi:!tender.incelendi})}
className="text-xs px-3 py-1.5 rounded-lg border border-slate-500 disabled:opacity-50">
{tender.incelendi ? 'İncelendi — yeniden aç' : 'İncelendi olarak işaretle'}
toggleTrackTender(tender.id)}
className={`flex items-center gap-1.5 font-bold px-3 py-1.5 rounded-lg text-xs transition-colors ${
tender.takipte ? 'bg-amber-400 text-slate-900' : 'bg-slate-700 text-white hover:bg-slate-600'
}`}
>
{tender.takipte ? 'Takipte' : 'Takip Et'}
{/* İhaleden göreve: teslim tarihi ihale saati; sonuç yayımlanınca bildirim */}
{tender.gorev ? (
✓ Görev açık{tender.gorev.teslim ? ` · ${tender.gorev.teslim}` : ''}
) : (
gorevAc(tender.id, { kontrolListesi: aiAnalysisResults?.documentChecklist || [] })}
title="Hazırlık görevi aç: teslim tarihi ihale saati olur, pano 'Teklif Hazırlanıyor' kolonuna düşer, sonuç yayımlanınca bildirim gelir"
className="flex items-center gap-1.5 font-bold px-3 py-1.5 rounded-lg text-xs transition-colors bg-amber-400 text-slate-950 hover:bg-amber-300"
>
🗂 Hazırlık Görevi Aç
)}
setActiveTenderDetail(null)}
aria-label="İhale detayını kapat"
className="text-slate-400 hover:text-white p-1"
>
{/* 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: 'Teklif Dosyası Hazırla', 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 (
setActiveTab(tab.id)}
className={`flex items-center gap-2 px-4 py-2.5 rounded-lg text-xs font-bold transition-all whitespace-nowrap ${
isActive
? 'bg-blue-600 text-white shadow-xs'
: 'text-slate-600 hover:bg-slate-100'
}`}
>
{tab.label}
{tab.badge && (
{tab.badge}
)}
);
})}
{/* 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) => (
gorevKontrol(tender.id, i)}
className={`text-xs text-left flex items-center gap-2 ${k.tamam ? 'line-through text-slate-400' : 'text-slate-800'}`}>
{k.ad}
))}
)}
{/* Ortak havuzdan teklif cetveli kalemleri */}
{/* Material Table */}
{tender.items?.length > 0 && (
Malzeme / Kalem Listesi ({tender.items?.length || 0} Kalem)
#
Malzeme Adı
Miktar
Tahmini Birim Fiyat
Toplam Tutarlar
{tender.items?.map((item, index) => (
{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}
))}
{/* Right Sidebar of Details: TCMB Kur & Calendar & Specs */}
{/* TCMB Kur Farkı Calculator */}
Kur Farkı Hesaplayıcı
{!fx ? 'Kur verisi bekleniyor' : fxIsStale ? 'TCMB · yenileme başarısız' : 'TCMB döviz satış'}
{fx &&
TCMB bülteni: {fx.bulletinDate} · Alındı: {new Date(fx.fetchedAt).toLocaleString('tr-TR')}{fxIsStale ? ' · Son başarılı veri; güncelleme başarısız' : ''}
}
{fxError &&
{fxError}
}
USD / TRY
{usdRate ? `₺${usdRate}` : '—'}
EUR / TRY
{eurRate ? `₺${eurRate}` : '—'}
Teklif Tutarı (TL)
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
setActiveTab('pdf_stamp')}
className="w-full bg-blue-600 hover:bg-blue-500 text-white font-bold py-2.5 px-3 rounded-lg text-xs flex items-center justify-center gap-2 shadow-2xs"
>
Teklif Dosyası Hazırla (PDF / Excel)
Takvim Dosyasını İndir (.ics)
Bu dosya takvime otomatik senkron göndermez. Tarih veya saat değişirse yeni dosyayı indirip Google/Outlook kaydını güncelleyin; aynı etkinlik kimliği ve sürümü destekleniyorsa mevcut kayıt güncellenir.
)}
{/* Tab 2: Teklif dosyası hazırlığı */}
{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'}
setIsAiSpecAnalyzerOpen(true)}
className="bg-amber-400 hover:bg-amber-300 text-slate-950 font-black px-4 py-2.5 rounded-xl text-xs flex items-center gap-2 transition-colors shadow-md cursor-pointer"
>
⚡ Şartname Yükle & AI Analizi Başlat
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' && (
)}
);
};