From fa28fb5f855dc56edb23c9fc544caee120940cb1 Mon Sep 17 00:00:00 2001 From: ht Date: Sun, 6 Sep 2026 12:47:11 +0300 Subject: [PATCH] Verify analysis recovery in browser and refresh new job status --- docs/quality/qa-general-analysis-recovery.json | 14 ++++++++++++++ docs/quality/uygulama-durumu.md | 7 +++++++ src/components/AiJobHistory.jsx | 9 ++++++++- src/components/AiSpecAnalyzerModal.jsx | 16 ++++------------ src/context/AppContext.jsx | 6 ++++-- src/services/ai.js | 4 ++-- tests/browser-qa.mjs | 12 ++++++++++-- 7 files changed, 49 insertions(+), 19 deletions(-) create mode 100644 docs/quality/qa-general-analysis-recovery.json diff --git a/docs/quality/qa-general-analysis-recovery.json b/docs/quality/qa-general-analysis-recovery.json new file mode 100644 index 0000000..2655057 --- /dev/null +++ b/docs/quality/qa-general-analysis-recovery.json @@ -0,0 +1,14 @@ +{ + "checks": { + "running": true, + "closed": true, + "completedAfterReload": true, + "automaticRunningList": true, + "friendlyUnavailable": true, + "restoredSummary": true, + "restoredSource": true, + "restoredFilename": true + }, + "scope": "Local browser UI with synthetic provider; real job storage and analysis executor; not real AI or production worker restart acceptance", + "snapshot": "- banner:\n - text: İhale\n - generic: ist\n - textbox \"İhale, kurum veya İKN no ile arayın...\"\n - button \"ARA\":\n - generic: ARA\n - button \"ÜRÜN ARA\":\n - generic: ÜRÜN ARA\n - button \"Detaylı İhale Arama\":\n - button \"AI Şartname Analizi\":\n - generic: AI Şartname Analizi\n - button \"Web Siteni Tara (AI)\":\n - generic: Web Siteni Tara (AI)\n - generic: 7 gün\n - button \"Bildirimler\":\n - button \"Hesap menüsü\":\n - generic: L\n - generic: Laboratuvar\n- generic: \"5\"\n- generic: Canlı İhale Havuzu\n- generic: \"5\"\n- generic: Yüklü İhale\n- generic: \"0\"\n- generic: Bugün Eklenen\n- generic: \"5\"\n- generic: Kalem\n- generic: \"0\"\n- generic: Sonuç Arşivi\n- generic: \"0\"\n- generic: Takibimdeki\n- button \"Sonraki sekmeler\":\n- button \"Ana İhale Listesi\":\n - generic: Ana İhale Listesi\n- button \"Günlük Bülten\":\n - generic: Günlük Bülten\n- button \"Sonuç Sorgula\":\n - generic: Sonuç Sorgula\n- button \"İhale Takvimi\":\n - generic: İhale Takvimi\n- button \"Sözleşme Sorgula\":\n - generic: Sözleşme Sorgula\n- button \"KDV Hesapla\":\n - generic: KDV Hesapla\n- button \"Sınır Değer\":\n - generic: Sınır Değer\n- button \"Fiyat Analizi\":\n - generic: Fiyat Analizi\n- button \"Kanban Pano Sürükle Takip\":\n - generic: Kanban Pano\n - generic: Sürükle Takip\n- button \"Rakip Radar Canlı Uyar\":\n - generic: Rakip Radar\n - generic: Canlı Uyar\n- button \"Doküman Deposu\":\n - generic: Doküman Deposu\n- button \"Analitik Dashboard\":\n - generic: Analitik Dashboard\n- button \"Kırmızı Bayrak Şeffaflık\":\n - generic: Kırmızı Bayrak\n - generic: Şeffaflık\n- button \"KİK Kurul Kararları\":\n - generic: KİK Kurul Kararları\n- button \"AB İhaleleri (TED)\":\n - generic: AB İhaleleri (TED)\n- button \"Planlar\":\n - generic: Planlar\n- button \"API & Webhooks\":\n - generic: API & Webhooks\n- button \"Mobil PWA\":\n - generic: Mobil PWA\n- main:\n - paragraph: AKTİF İHALELER\n - paragraph: \"5\"\n - paragraph: BUGÜN EKLENENLER\n - paragraph: \"0\"\n - paragraph: TAKİP ETTİKLERİM\n - paragraph: \"0\"\n - generic \"İlk ziyaretiniz — bugün yayımlanan ilanlar gösteriliyor\":\n - paragraph: SON GİRİŞTEN BERİ\n - paragraph: \"0\"\n - paragraph: İNCELENMEMİŞ\n - paragraph: \"5\"\n - complementary:\n - text: Hızlı Erişim\n - button \"Güncel Tüm İhaleler\":\n - generic: Güncel Tüm İhaleler\n - button \"Favori Kurumlarım\":\n - generic: Favori Kurumlarım\n - button \"Doğrudan Temin Popüler\":\n - generic: Doğrudan Temin\n - generic: Popüler\n - button \"İhaleler (Açık/Pazarlık)\":\n - generic: İhaleler (Açık/Pazarlık)\n - button \"Bugün Eklenenler\":\n - generic: Bugün Eklenenler\n - button \"İncelediğim İhaleler\":\n - generic: İncelediğim İhaleler\n - button \"İncelemediklerim\":\n - generic: İncelemediklerim\n - button \"Sözleşme İmzalananlar\":\n - generic: Sözleşme İmzalananlar\n - button \"Sonuçlananlar\":\n - generic: Sonuçlananlar\n - button \"İptal Edilenler\":\n - generic: İptal Edilenler\n - button \"İhale Arşivi\":\n - generic: İhale Arşivi\n - text: Kayıtlı Filtrelerim\n - generic: 0 Bot\n - paragraph: Henüz aktif bir filtre yok.\n - button \"Yeni Filtre Kur\":\n - generic: Yeni Filtre Kur\n - button \"Alım Türü\":\n - text: Alım Türü\n - radio \"Tümü\" [checked]\n - generic: Tümü\n - radio \"Doğrudan Temin\"\n - generic: Doğrudan Temin\n - radio \"Açık İhale\"\n - generic: Açık İhale\n - radio \"Pazarlık Usulü\"\n - generic: Pazarlık Usulü\n - button \"Sektörler\":\n - text: Sektörler\n - textbox \"Sektör ara…\"\n - button \"Tüm Sektörler\"\n - checkbox \"Akaryakıt & Yakıt\"\n - generic: Akaryakıt & Yakıt\n - checkbox \"Altyapı & Yol\"\n - generic: Altyapı & Yol\n - checkbox \"Asansör & Yürüyen Sistem\"\n - generic: Asansör & Yürüyen Sistem\n - checkbox \"Atölye & Makine Ekipmanı\"\n - generic: Atölye & Makine Ekipmanı\n - checkbox \"Bilişim & Yazılım\"\n - generic: Bilişim & Yazılım\n - checkbox \"Danışmanlık & Proje\"\n - generic: Danışmanlık & Proje\n - checkbox \"Elektrik & Enerji\"\n - generic: Elektrik & Enerji\n - checkbox \"Gıda & Catering\"\n - generic: Gıda & Catering\n - checkbox \"Güvenlik Sistemleri\"\n - generic: Güvenlik Sistemleri\n - checkbox \"Isıtma & İklimlendirme\"\n - generic: Isıtma & İklimlendirme\n - checkbox \"İnşaat & Bakım\"\n - generic: İnşaat & Bakım\n - checkbox \"Kırtasiye & Büro\"\n - generic: Kırtasiye & Büro\n - checkbox \"Laboratuvar & Ölçüm\"\n - generic: Laboratuvar & Ölçüm\n - checkbox \"Mobilya & Donatı\"\n - generic: Mobilya & Donatı\n - checkbox \"Otomotiv & İş Makinesi\"\n - generic: Otomotiv & İş Makinesi\n - checkbox \"Personel Hizmet Alımı\"\n - generic: Personel Hizmet Alımı\n - checkbox \"Peyzaj & Park\"\n - generic: Peyzaj & Park\n - checkbox \"Reklam & Organizasyon\"\n - generic: Reklam & Organizasyon\n - checkbox \"Sağlık / Tıbbi Malzeme\"\n - generic: Sağlık / Tıbbi Malzeme\n - checkbox \"Sigorta\"\n - generic: Sigorta\n - checkbox \"Spor Tesisleri\"\n - generic: Spor Tesisleri\n - checkbox \"Su & Arıtma\"\n - generic: Su & Arıtma\n - checkbox \"Tarım & Hayvancılık\"\n - generic: Tarım & Hayvancılık\n - checkbox \"Taşımacılık & Kiralama\"\n - generic: Taşımacılık & Kiralama\n - checkbox \"Taşınmaz & Kiralama\"\n - generic: Taşınmaz & Kiralama\n - checkbox \"Tekstil & Giyim\"\n - generic: Tekstil & Giyim\n - checkbox \"Temizlik & Hijyen\"\n - generic: Temizlik & Hijyen\n - checkbox \"Yayın & Arşiv\"\n - generic: Yayın & Arşiv\n - checkbox \"Genel\"\n - generic: Genel\n - button \"CPV Kodu\":\n - text: CPV Kodu\n - button \"Kurum Tipleri\":\n - text: Kurum Tipleri\n - button \"Şehir Seçimi\":\n - text: Şehir Seçimi\n - heading \"İhale Listesi (5 Sonuç)\" [level=2]:\n - text: İhale Listesi\n - generic: (5 Sonuç)\n - generic \"Süresi geçtiği bilinen, iptal edilen ve sonuçlanan kayıtlar gizlenir. Tarihi veya saati bilinmeyen kayıtlar ayrıca doğrulanmalıdır.\":\n - checkbox \"Süresi geçenleri gizle\" [checked]\n - text: Süresi geçenleri gizle\n - combobox \"İhale sıralaması\":\n - option \"Şirketime En Uyumlu\"\n - 'option \"Yayın Tarihi: En Yeni\" [selected]'\n - option \"Tarihi Yaklaşanlar (Acil)\"\n - option \"Tarihi Uzak Olanlar\"\n - button \"Excel Hazırla\":\n - generic: Excel Hazırla\n - paragraph: Yayın tarihi bilinmeyen kayıtlar sonda gösterilir. Aynı gün içinde saati bilinen kayıtlar önce gelir.\n - generic: İhale\n - generic: \"10\"\n - generic: \"09\"\n - generic: 10:00\n - heading \"QA — Giresun laboratuvar\" [level=3]:\n - button \"QA — Giresun laboratuvar\"\n - button \"Takibe Al\":\n - generic: Giresun TEST KURUMU\n - button \"Kurumu favorilere ekle\":\n - generic: Giresun\n - generic \"İhalenin yapılacağı tarih\":\n - generic: \"İhale: 10.09.2026 10:00\"\n - generic: \"# 2026/1582991\"\n - generic: Mal Alımı\n - generic: Laboratuvar & Ölçüm\n - generic: Sentetik test kalemi\n - generic: 2 adet\n - generic: İhale\n - generic: \"22\"\n - generic: \"09\"\n - generic: 11:00\n - heading \"QA — Kayseri laboratuvar\" [level=3]:\n - button \"QA — Kayseri laboratuvar\"\n - button \"Takibe Al\":\n - generic: Kayseri TEST KURUMU\n - button \"Kurumu favorilere ekle\":\n - generic: Kayseri\n - generic \"İhalenin yapılacağı tarih\":\n - generic: \"İhale: 22.09.2026 11:00\"\n - generic: \"# 2026/1534665\"\n - generic: Mal Alımı\n - generic: Laboratuvar & Ölçüm\n - generic: Sentetik test kalemi\n - generic: 2 adet\n - generic: İhale\n - generic: \"22\"\n - generic: \"09\"\n - generic: 10:30\n - heading \"QA — Akdeniz laboratuvar\" [level=3]:\n - button \"QA — Akdeniz laboratuvar\"\n - button \"Takibe Al\":\n - generic: Akdeniz TEST KURUMU\n - button \"Kurumu favorilere ekle\":\n - generic: Akdeniz\n - generic \"İhalenin yapılacağı tarih\":\n - generic: \"İhale: 22.09.2026 10:30\"\n - generic: \"# 2026/1499061\"\n - generic: Mal Alımı\n - generic: Laboratuvar & Ölçüm\n - generic: Sentetik test kalemi\n - generic: 2 adet\n - generic: İhale\n - generic: \"09\"\n - generic: \"09\"\n - generic: 10:00\n - heading \"QA — Aksaray laboratuvar\" [level=3]:\n - button \"QA — Aksaray laboratuvar\"\n - button \"Takibe Al\":\n - generic: Aksaray TEST KURUMU\n - button \"Kurumu favorilere ekle\":\n - generic: Aksaray\n - generic \"İhalenin yapılacağı tarih\":\n - generic: \"İhale: 09.09.2026 10:00\"\n - generic: \"# 2026/1573235\"\n - generic: Mal Alımı\n - generic: Laboratuvar & Ölçüm\n - generic: Sentetik test kalemi\n - generic: 2 adet\n - generic: İhale\n - generic: \"11\"\n - generic: \"09\"\n - generic: 10:00\n - heading \"QA — Tepecik laboratuvar\" [level=3]:\n - button \"QA — Tepecik laboratuvar\"\n - button \"Takibe Al\":\n - generic: Tepecik TEST KURUMU\n - button \"Kurumu favorilere ekle\":\n - generic: Tepecik\n - generic \"İhalenin yapılacağı tarih\":\n - generic: \"İhale: 11.09.2026 10:00\"\n - generic: \"# 2026/1523851\"\n - generic: Mal Alımı\n - generic: Laboratuvar & Ölçüm\n - generic: Sentetik test kalemi\n - generic: 2 adet\n - paragraph: Tüm kayıtlar gösterildi — 5 ihale\n - complementary:\n - text: Firmanıza Ait Kayıtlar\n - generic: İncelediklerim\n - generic: \"0\"\n - generic: Sözleşmelerim\n - generic: \"0\"\n - generic: Sonuçlarım\n - generic: \"0\"\n - generic: Takibimdekiler\n - generic: \"0\"\n - text: Yaklaşan İhale Tarihleri\n - paragraph: 14 gün içinde yapılacak takipli ihale yok. Yıldızla takibe alın; burada geri sayım başlar.\n - text: İhale Durumlarım\n - paragraph: Takibe aldığınız ihalelere durum atayabilir, ekip içinde sürükle-bırak ile iş dağıtabilirsiniz.\n - button \"Kanban Panosunu Aç\":\n - generic: Kanban Panosunu Aç\n - text: Rakip Radar & Pazar Payı\n - generic: Sektör Analizi\n - paragraph: Kazanan firma verisi biriktikçe (doğrudan temin & sonuç ilanları) gerçek pazar payları burada listelenir.\n - button \"Tüm Rakip Sözleşmelerini İncele\":\n - generic: Tüm Rakip Sözleşmelerini İncele\n - heading \"Ekip Yönetimi (0)\" [level=3]:\n - text: Ekip Yönetimi (0)\n - textbox \"Ad Soyad\"\n - textbox \"E-posta (ops.)\"\n - combobox:\n - option \"Uzman\" [selected]\n - option \"Yönetici\"\n - option \"Saha\"\n - option \"Muhasebe\"\n - button \"Ekip Üyesi Ekle\":\n - text: Ekip Üyesi Ekle\n - heading \"Teknik Şartname & Teklif Analiz Asistanı\" [level=2]\n - generic: GLM 5.3 Flash (Z.ai)\n - paragraph: Şartname dosyanızı yükleyin; yeterlik, riskler ve teklif aralığı gerçek AI ile analiz edilsin.\n - button \"Analiz ekranını kapat\":\n - generic \"Kayıtlı AI işleri\"\n - paragraph: Ekran kapansa da sunucudaki işler devam eder. Son 50 iş gösterilir. Kesilen işi yeniden başlatmak sağlayıcıya yeni istek gönderir. Eski sonucu açmak asistanın mevcut metinlerini değiştirir.\n - button \"İş listesini yenile\"\n - list:\n - paragraph: Şartname analizi · Tamamlandı · 06.09.2026 12:46:35\n - button \"Kaynak ve sonucu aç\"\n - paragraph: Şartname analizi · Tamamlandı · 06.09.2026 12:45:37\n - button \"Kaynak ve sonucu aç\"\n - generic \"Kaydedilen analiz kaynak metni\" [active]\n - generic: QA — Kayseri laboratuvar Kayseri TEST KURUMU Bu yalnızca yerel arayüz test verisidir. Teklif geçerlilik süresi 120 takvim günü. Birim fiyat teklif. Bu yalnızca yerel arayüz test verisidir. Teklif geçerlilik süresi 120 takvim günü. Birim fiyat teklif. Bu yalnızca yerel arayüz test verisidir. Teklif geçerlilik süresi 120 takvim günü. Birim fiyat teklif.\n - alert: AI hizmeti şu anda kullanılamıyor. Kayıtlı sonuçlarınızı açabilirsiniz. Yeni analiz için daha sonra tekrar deneyin veya uygulama yöneticisine bildirin.\n - heading \"Teknik Şartname Dosyası Yükleyin\" [level=3]\n - paragraph:\n - text: Metin tabanlı\n - code: .pdf\n - text: veya\n - code: .txt\n - text: dosyası — belge tarayıcınızda okunur, yalnızca metni analiz için gönderilir.\n - button \"Derin Analiz + Yol Haritası\"\n - button \"Hızlı Özet\"\n - paragraph: Elenme sebepleri, gözden kaçan ince detaylar, belge kontrol listesi, kritik tarihler ve teklife kadar adım adım yol haritası üretilir. Daha uzun sürer.\n - generic: Dosya Seç (PDF / TXT)\n - text: Şartname Özeti\n - generic: Derin Analiz\n - generic: QA (sentetik) · 06.09.2026 12:46:43\n - paragraph: \"QA kayıtlı genel analiz: Laboratuvar kaynak metni korundu.\"\n - paragraph: \"Kaynak: 2026/1534665 — ilan metni\"\n - heading \"Yeterlik Kriterleri & Zorunlu Belgeler\" [level=4]:\n - text: Yeterlik Kriterleri & Zorunlu Belgeler\n - list:\n - listitem: Bu bölüm için yapılandırılmış yeterlik listesi çıkarılamadı. Derin analizdeki belge ve şartları kontrol edin.\n - heading \"Riskler & Cezai Hükümler\" [level=4]:\n - text: Riskler & Cezai Hükümler\n - paragraph: Bu bölümde yapılandırılmış ceza/risk listesi yok. Bu, belgede risk bulunmadığı anlamına gelmez.\n - paragraph: Analiz yapay zeka tarafından üretilmiştir; teklif kararı öncesi şartnamenin aslını doğrulayın.\n - button \"Kapat\"\n- button \"Kullanım kılavuzu (? tuşu)\":" +} \ No newline at end of file diff --git a/docs/quality/uygulama-durumu.md b/docs/quality/uygulama-durumu.md index 107b222..d526602 100644 --- a/docs/quality/uygulama-durumu.md +++ b/docs/quality/uygulama-durumu.md @@ -747,3 +747,10 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö - Oturum başına eşzamanlı genel analiz başlatma kilidi eklendi; eski oturum işi yeni oturumun yükleniyor durumunu veya sonucunu değiştiremez. Farklı ihale seçilince önceki ihalenin analiz sonucu gösterilmez. - Doğrulama: 167 otomatik test ve üretim derlemesi geçti. Yeni testler kaynak sabitleme, ihale bazlı iş bulma, sunucu incelemesi kullanımı, inceleme değişikliği ve iptal davranışını kapsıyor. Büyük paket uyarısı sürüyor. - Bu yeni genel analiz ekranı için tarayıcıda kapat/aç/yenile kabul testi ve gerçek sağlayıcı testi henüz yapılmadı. Son 50 kayıt sınırı ve kesilen işi baştan başlatma davranışı sürüyor; ara parçadan devam etme yok. D06 ve tüm plan açık. + +### Genel analiz tarayıcı kabulü ve liste güncellemesi — 6 Eylül 2026 +- Yerel 5179 QA ortamında Giresun ve Kayseri sentetik ilanlarıyla genel analiz başlatıldı. Analiz ekranı kapatıldı, sayfa yeniden yüklendi, kontrollü sağlayıcı yanıtı tamamlandı; kayıtlı kaynak metni/dosya adı ve sonuç tekrar açıldı. +- İlk denemede yeni iş listede elle yenilemeden görünmüyordu. İstemci iş durumu değişikliklerini context'e aktarıyor; liste etkisi önceki devam eden okumadan bağımsız yenileniyor ve geç eski okumayı atıyor. İkinci denemede elle yenilemeden çalışan iş göründü. +- Sunucu anahtarı anlatan teknik uyarı normal kullanıcı için hizmet durumu mesajıyla değiştirildi. Simgeli kapatma düğmesi erişilebilir ad aldı. +- Kanıt: `qa-general-analysis-recovery.json`, 8 kontrol doğru. 167 otomatik test ve üretim derlemesi geçti. +- QA yalnız sentetik sağlayıcı sonucu kullanır; gerçek SQLite iş kaydı/okuması ve genel analiz yürütücüsü kullanıldı. İş başlatma QA tarafından tutulur: üretim işçi yaşam döngüsü, gerçek AI doğruluğu veya sunucu yeniden başlatma kabulü olarak değerlendirilmez. diff --git a/src/components/AiJobHistory.jsx b/src/components/AiJobHistory.jsx index 1e9f452..4a6ab56 100644 --- a/src/components/AiJobHistory.jsx +++ b/src/components/AiJobHistory.jsx @@ -11,7 +11,14 @@ export function AiJobHistory({ikn,onOpen,revision,locked=false,operationFilter=' try {await fn();}catch(e){if(alive.current)setError(e.message);}finally {if(alive.current)setBusy(false);} }; const refresh=async()=>{const data=await listAiJobs(ikn);if(!Array.isArray(data.jobs))throw new Error('İş listesi alınamadı.');if(alive.current)setJobs(data.jobs.filter(job=>operationFilter==='offer'?job.operation!=='analiz':job.operation===operationFilter));}; - useEffect(()=>{void act(refresh);},[ikn,revision,operationFilter]); + useEffect(()=>{ + let current=true;setBusy(true);setError(null); + listAiJobs(ikn).then(data=>{ + if(!Array.isArray(data.jobs))throw new Error('İş listesi alınamadı.'); + if(current)setJobs(data.jobs.filter(job=>operationFilter==='offer'?job.operation!=='analiz':job.operation===operationFilter)); + }).catch(e=>{if(current)setError(e.message);}).finally(()=>{if(current)setBusy(false);}); + return()=>{current=false;}; + },[ikn,revision,operationFilter]); return
Kayıtlı AI işleri

Ekran kapansa da sunucudaki işler devam eder. Son 50 iş gösterilir. Kesilen işi yeniden başlatmak sağlayıcıya yeni istek gönderir. Eski sonucu açmak asistanın mevcut metinlerini değiştirir.

diff --git a/src/components/AiSpecAnalyzerModal.jsx b/src/components/AiSpecAnalyzerModal.jsx index 18fa959..d41de2b 100644 --- a/src/components/AiSpecAnalyzerModal.jsx +++ b/src/components/AiSpecAnalyzerModal.jsx @@ -12,7 +12,7 @@ const tl = (n) => (typeof n === 'number' && isFinite(n) ? '₺' + n.toLocaleStri export const AiSpecAnalyzerModal = () => { const { isAiSpecAnalyzerOpen, setIsAiSpecAnalyzerOpen, - analyzeSpecPdfWithGemini, openSavedAnalysis, authUser, aiAnalysisResults, aiAnalyzing, activeAiModel, activeTenderDetail, + analyzeSpecPdfWithGemini, openSavedAnalysis, authUser, aiAnalysisResults, aiAnalyzing, aiJobStatus, activeAiModel, activeTenderDetail, } = useApp(); const [isAnalyzing, setIsAnalyzing] = useState(false); @@ -114,29 +114,21 @@ export const AiSpecAnalyzerModal = () => {

Şartname dosyanızı yükleyin; yeterlik, riskler ve teklif aralığı gerçek AI ile analiz edilsin.

-
{const result=await openSavedAnalysis(job);setRestoredSource({text:job.input.text,tenderKey:result.tenderKey});setSelectedFile(result.fileName || null);setCustomBid(result.bidAnalysis?.recommendedBid ?? null);setError(null);}} /> {results && restoredSource?.tenderKey===results.tenderKey &&
Kaydedilen analiz kaynak metni
{restoredSource.text || 'Bu eski kayıtta kaynak metni bulunmuyor.'}
} {mesgul&&

Analiz sunucuda sürüyor. Bu ekranı kapatabilirsiniz; sonucu Kayıtlı AI işleri bölümünden açabilirsiniz.

} {/* AI yapılandırılmamış uyarısı */} {aiCfg && !aiCfg.configured && ( -
- -

- AI henüz yapılandırılmamış. En kolayı: sunucuya tek bir{' '} - OPENROUTER_API_KEY ekleyin — - zincirdeki tüm modeller (GLM, DeepSeek, Gemini, Grok, GPT, Claude) bu tek anahtarla çalışır. - Dilerseniz tek tek sağlayıcı anahtarı da verebilirsiniz; doğrudan anahtar OpenRouter'ı ezer. - Model sırası Admin → AI Model Ayarları'ndan belirlenir.

-
+

AI hizmeti şu anda kullanılamıyor. Kayıtlı sonuçlarınızı açabilirsiniz. Yeni analiz için daha sonra tekrar deneyin veya uygulama yöneticisine bildirin.

)} {/* Uploader */} diff --git a/src/context/AppContext.jsx b/src/context/AppContext.jsx index 313db72..28450a2 100644 --- a/src/context/AppContext.jsx +++ b/src/context/AppContext.jsx @@ -159,7 +159,7 @@ export const AppProvider = ({ children }) => { // Giriş yapınca kullanıcı verisini SUNUCUDAN yükle (localStorage'ı ezer) useEffect(() => { - aiRunRef.current=null;setAiAnalyzing(false); + aiRunRef.current=null;setAiAnalyzing(false);setAiJobStatus(''); setVeriSenkron(false); setPlan(null); setSaveError(null); setFavoriteKurumlar([]); setTeamMembers([]); setSavedFilters([]); setCompetitorWatchlist([]); setReminderSettings(defaultReminder()); setUserProfile(defaultProfile(authUser)); @@ -338,6 +338,7 @@ export const AppProvider = ({ children }) => { // tetiklenebiliyor; modalın beklerken boş görünmemesi için ortak sinyal. const [aiAnalyzing, setAiAnalyzing] = useState(false); const aiRunRef=useRef(null); + const [aiJobStatus,setAiJobStatus]=useState(''); // Enterprise Module States // Rakip izleme listesi — kullanıcı gerçek kazanan firma analizinden ekler (hesaba özel sunucu kaydı) @@ -454,7 +455,7 @@ export const AppProvider = ({ children }) => { setAiAnalysisResults(null); try { const { analyzeSpecText } = await import('../services/ai'); - const data = await analyzeSpecText({ text, fileName, tender, chain: aiModelConfig.chain, mode, extraction, reviewedSource }); + const data = await analyzeSpecText({ text, fileName, tender, chain: aiModelConfig.chain, mode, extraction, reviewedSource },{onProgress:job=>{if(getToken()===session && aiRunRef.current===run)setAiJobStatus(`${job.id}:${job.state}`);}}); const {analysisView}=await import('../services/ai'); const result=analysisView(data,{tender,mode,fileName}); if(getToken() !== session || aiRunRef.current!==run) throw new Error('Oturum değişti; analiz sonucu bu hesaba aktarılmadı.'); @@ -785,6 +786,7 @@ export const AppProvider = ({ children }) => { openSavedAnalysis, aiAnalysisResults, aiAnalyzing, + aiJobStatus, competitorWatchlist, setCompetitorWatchlist, vaultDocuments, diff --git a/src/services/ai.js b/src/services/ai.js index b5f328b..a3dcd72 100644 --- a/src/services/ai.js +++ b/src/services/ai.js @@ -14,10 +14,10 @@ export async function getAiConfig() { } } -export async function analyzeSpecText(input) { +export async function analyzeSpecText(input,{onProgress}={}) { const session=getToken(); const check=()=>{if(getToken()!==session)throw new Error('Oturum değişti; kayıtlı analiz bu hesaba aktarılmadı.');}; - return runDurableAiJob('analiz',input,{check}); + return runDurableAiJob('analiz',input,{check,onProgress}); } export function analysisView(data,input) { diff --git a/tests/browser-qa.mjs b/tests/browser-qa.mjs index 943c9f0..d78b554 100644 --- a/tests/browser-qa.mjs +++ b/tests/browser-qa.mjs @@ -17,6 +17,7 @@ let apiKeyListFailure=false; process.env.IHALEIST_USER_DATA_DIR=dir; const qaJobs=await import('../server/ai-job-store.mjs'); const {qualifyOfferDraft}=await import('../server/offer-draft.mjs'); +const {executeAnalysisJob}=await import('../server/analysis-job.mjs'); const pendingQaJobs=new Map(); const qaHeartbeat=setInterval(()=>{for(const job of pendingQaJobs.values())qaJobs.heartbeatAiJob(job.id,job.runId);},5000);qaHeartbeat.unref(); const probe=netServer();await new Promise(r=>probe.listen(0,'127.0.0.1',r));const port=probe.address().port;await new Promise(r=>probe.close(r)); @@ -28,9 +29,16 @@ process.env.IHALEIST_SHARED_DATA_DIR=join(dir,'shared'); const {observeTenders}=await import('../server/tender-history.mjs'); observeTenders(tenders); for(let revision=1;revision<=22;revision++) observeTenders([{...tenders[0],saat:revision%2?'11:00':'10:00'}]); -const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{target:'http://127.0.0.1:'+port}}},plugins:[{name:'qa-fixtures',configureServer(server){server.middlewares.use((req,res,next)=>{ +const ui=await createServer({server:{host:'127.0.0.1',port:0,proxy:{'/api':{target:'http://127.0.0.1:'+port}}},plugins:[{name:'qa-fixtures',configureServer(server){server.middlewares.use(async(req,res,next)=>{ if(req.url==='/qa-ai-controls') {res.setHeader('Content-Type','text/html; charset=utf-8');res.end(`

Yerel AI işi testi

Sağlayıcı taklidi kullanılır; ücretli AI çağrısı yapılmaz.

`);return;} - if(req.url==='/qa-ai-complete' && req.method==='POST') {for(const job of pendingQaJobs.values())qaJobs.finishAiJob(job.id,job.runId,{success:true,...qualifyOfferDraft({bolumler:[{baslik:'Teklif Mektubu (Kapak)',icerik:'QA kayıtlı sonuç: Laboratuvar teklif taslağı. Bu sentetik metin resmi teklif değildir.'}]},['Teklif Mektubu (Kapak)'])});pendingQaJobs.clear();res.end('QA işleri tamamlandı');return;} + if(req.url==='/qa-ai-complete' && req.method==='POST') { + for(const job of pendingQaJobs.values()) { + const result=job.operation==='analiz' ? await executeAnalysisJob(job,{pin:qaJobs.pinAiJobInput,analyze:async()=>({result:{summary:'QA kayıtlı genel analiz: Laboratuvar kaynak metni korundu.',items:[],requiredDocuments:[],roadmap:[]},mode:job.input.mode,model:{label:'QA',provider:'sentetik',model:'qa'},attempts:[]})}) + : {success:true,...qualifyOfferDraft({bolumler:[{baslik:'Teklif Mektubu (Kapak)',icerik:'QA kayıtlı sonuç: Laboratuvar teklif taslağı. Bu sentetik metin resmi teklif değildir.'}]},['Teklif Mektubu (Kapak)'])}; + qaJobs.finishAiJob(job.id,job.runId,result); + } + pendingQaJobs.clear();res.end('QA işleri tamamlandı');return; + } if(req.url==='/api/me/ai-jobs' && req.method==='POST') { if(req.headers.authorization!=='Bearer '+token){res.writeHead(401);res.end('{}');return;} let body='';req.on('data',chunk=>body+=chunk);req.on('end',()=>{try{