Panelde seçilen modeller tıkanınca (limit/429) şartname analizi ve teklif asistanı boş kalıyordu; analyzeSpec zincire yoksa 'gemini'yi sona ekler. Panel ve kılavuz notu. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
150 lines
14 KiB
React
150 lines
14 KiB
React
import { createPortal } from 'react-dom';
|
||
import React, { useEffect, useState } from 'react';
|
||
import { useApp } from '../context/AppContext';
|
||
import { HelpCircle, X, BookOpen, Lightbulb, ListChecks } from 'lucide-react';
|
||
|
||
/**
|
||
* Kullanım kılavuzu — sağdan açılan yan panel, açık sekmeye göre içerik.
|
||
* Her sayfada aynı düğme (sağ altta "?"); "?" tuşu da açar. İçerik kod
|
||
* içinde tutuluyor ki özellik değişince aynı commit'te güncellensin.
|
||
*/
|
||
const KILAVUZ = {
|
||
list: {
|
||
baslik: 'Ana İhale Listesi',
|
||
ne: 'Türkiye kamu ihaleleri (ilan.gov.tr + EKAP), doğrudan temin ve geçmiş sonuçlar tek listede. Kartta ihale/ilan tarihi, sektör, CPV, teklif cetveli kalemleri ve şirket uyum skoru görünür.',
|
||
adimlar: [
|
||
'Sağ paneldeki \'Yaklaşan İhale Tarihleri\' kutusu yıldızladığınız / panoya aldığınız ihalelerin 14 günlük geri sayımını gösterir (≤3 gün kırmızı, ≤7 gün turuncu); tıklayınca detay açılır.',
|
||
'Detaydaki \'Teklif Asistanı\' sekmesi: Belgeye Sor (dokümanda teminat, teslim, ceza gibi soruları [n] atıflı yanıtlar), Taslağı Denetle (teklif metninizi gerekliliklerle karşılaştırır: karşılanıyor / eksik / belirsiz), Taslak Üret (kapak / teknik / ticari / tam dosya; bilinmeyen yerler [DOLDURUN]).',
|
||
'Üstteki kutuya ürün, kurum ya da İKN yazın — arama başlıkla birlikte teklif cetveli kalemlerinde de yapılır.',
|
||
'Başlıkta geçmeyen ürününüz için sarı "ÜRÜN ARA" düğmesini kullanın: EKAP dokümanlarının içinde arar.',
|
||
'Sol menüden il, tür, sektör, CPV/OKAS ile daraltın; "Sadece açık ihaleler" varsayılan açıktır.',
|
||
'Sıralamayı "Şirketime En Uyumlu" yapın — profilinizdeki ürünlere göre puanlar.',
|
||
'Karta tıklayıp detayda dokümanı yükleyin: kalemler otomatik çıkarılır, derin analiz ve yol haritası oradan.',
|
||
],
|
||
ipuclari: ['Teklif Asistanı doküman metnini EKAP\'tan alır; alamazsa sekmedeki \'Doküman yükle\' ile PDF\'i verin.', 'Yıldız = takip. Takipteki ihalede zeyilname/düzeltme ilanı çıkarsa ve sonuçlanınca bildirim gelir.', '"%N Uyumlu" rozetine gelince neden eşleştiği görünür.'],
|
||
},
|
||
bulten: { baslik: 'Günlük Bülten', ne: 'Bugün eklenen ihalelerin özeti.', adimlar: ['Sabah bir bakış için tasarlandı; e-posta bülteni için "Mobil PWA → Bildirimler"i kullanın.'], ipuclari: [] },
|
||
sonuc: { baslik: 'Sonuç Sorgula', ne: 'EKAP sonuç ilanlarından kazanan firma, sözleşme bedeli, teklif sayısı.', adimlar: ['Firma, idare veya il ile filtreleyin.', 'Rakibinizin hangi idarelerden ne bedelle aldığını görün; "Rakip Radar"a ekleyin.'], ipuclari: ['Kısmi ihalelerde bedel kısım başına, yaklaşık maliyet tüm ihale içindir — kırım oranı bu yüzden yalnız kısımsız ihalelerde gösterilir.'] },
|
||
takvim: { baslik: 'İhale Takvimi', ne: 'Takipteki ve açık ihalelerin tarih görünümü.', adimlar: ['Detaydan .ics indirip kendi takviminize ekleyebilirsiniz.'], ipuclari: [] },
|
||
sozlesme: { baslik: 'Sözleşme Sorgula', ne: 'İmzalanmış sözleşmelerin sorgusu.', adimlar: ['Firma adıyla arayın; aynı firmanın diğer sözleşmeleri listelenir.'], ipuclari: [] },
|
||
kdv: { baslik: 'KDV Hesapla', ne: 'KDV dahil/hariç dönüşüm.', adimlar: ['Tutarı girin, oranı seçin.'], ipuclari: [] },
|
||
sinir: {
|
||
baslik: 'Sınır Değer',
|
||
ne: 'Aşırı düşük teklif sınır değeri — Kamu İhale Genel Tebliği md. 79 (hizmet/mal) ve 45 (yapım) formülleriyle. Teklifiniz sınırın altında kalırsa idare açıklama ister.',
|
||
adimlar: ['Alım türünü seçin (mal alımında da hizmet formülü uygulanır — md. 62/A.1).', 'Yaklaşık maliyeti ve ilk oturumda okunan geçerli teklifleri girin.', 'Hizmet/mal için R katsayısını (2026 KİK tablosu) ya da yapım için N katsayısını seçin.', 'Kendi teklifinizi girin: sınırın altında mı, üstünde mi anında görünür.'],
|
||
ipuclari: ['Yapım hesabı Tebliğ\'in kendi örneğiyle (52.281,00 TL) birebir doğrulanmıştır.', 'R katsayıları her yıl 1 Şubat\'ta yenilenir.'],
|
||
},
|
||
fiyat: { baslik: 'Fiyat Analizi', ne: 'Geçmiş sonuçlardan kalem/sektör bazlı fiyat eğilimleri.', adimlar: ['Ürün adıyla arayın; benzer kalemlerin sözleşme bedellerini karşılaştırın.'], ipuclari: [] },
|
||
kanban: {
|
||
baslik: 'Kanban Pano',
|
||
ne: 'Takipteki ihalelerin süreç panosu: İncelenecek → İnceleniyor → Teklif Hazırlanıyor → Verildi → Kazanıldı/Kaybedildi.',
|
||
adimlar: ['Kartları sürükleyerek kolon değiştirin.', 'İhale detayında "Hazırlık Görevi Aç" deyin: teslim tarihi ihale saati olur, kartta geri sayım başlar (BUGÜN / 2 gün / süresi geçti).', 'Derin analizden gelen belge kontrol listesi görevin içinde işaretlenir.'],
|
||
ipuclari: ['Görev açılmış ihale sonuçlanınca bildirim gelir: kim, kaça, %kaç kırım.'],
|
||
},
|
||
rakip_radar: { baslik: 'Rakip Radar', ne: 'Kazanan firmalar, kırım oranı, ortalama teklif sayısı, tek teklifle kazanma oranı.', adimlar: ['"Radara Ekle" ile izleyin; teklif verince/kazanınca uyarı alın.', '"⛔ yasaklı sorgula" KİK sorgusunu açar, firma adını panoya kopyalar.'], ipuclari: ['Kırım oranı yalnız kısımsız ihalelerde hesaplanır — kısmi ihalede sayı yanıltıcı olurdu.'] },
|
||
dokuman_deposu: {
|
||
baslik: 'Doküman Deposu',
|
||
ne: 'Şirket belgeleriniz (imza sirküleri, oda kaydı…) ve ihale dokümanları sunucuda saklanır.',
|
||
adimlar: [
|
||
'Derin analizdeki \'Belge Kontrol Listesi\' her madde için depodaki evrakla eşleşir: Depoda hazır / Yakında dolacak (≤30 gün) / Süresi dolmuş / Depoda yok — rozetin üzerine gelince hangi belge ve kaç gün kaldığı görünür.','Her belgeye son kullanma tarihi girin — süresi dolan/30 gün içinde dolacak belgeler kırmızı/sarı uyarıyla listelenir, girişte bildirim gelir.', '"İhale Dokümanlarını Toplu Yükle": EKAP\'tan indirdiğiniz şartnameleri hepsini birden seçin; her dosyanın İKN\'si metninden bulunur, teklif cetveli kalemleri otomatik çıkarılır.', 'Taranmış (görüntü) PDF\'ler de okunur — OCR otomatik devreye girer.'],
|
||
ipuclari: ['Kategoriyi doğru seçin (Yasal / Kalite / Mali / Resmi / Teknik): eşleştirme önce kategoriye, sonra belge adına bakar.', 'Kalemleri çıkarılan ihale herkesin kartında kalemleriyle görünür; ilk yükleyen herkes için açar.'],
|
||
},
|
||
analitik_dashboard: { baslik: 'Analitik Dashboard', ne: 'Sektör, il ve kurum bazlı dağılımlar, hacim ve rekabet grafikleri.', adimlar: ['Filtreler listedekiyle aynıdır; sol menü seçimleri buraya da yansır.'], ipuclari: [] },
|
||
kirmizi_bayrak: { baslik: 'Kırmızı Bayrak', ne: 'Şeffaflık sinyalleri: tek teklifli ihaleler, düşük kırım, tahmin üstü bedel, tedarikçi yoğunlaşması.', adimlar: [
|
||
'Yeni kurallar: Kısa süre (ilan→ihale <7 gün), Hafta sonu tarih, Geç sözleşme (ihaleden 180+ gün sonra), Kazananı gizli (sonuç var ad yok), Parçalama şüphesi (aynı idare + sektörde 30 günde 3+ doğrudan temin — 4734 md. 5 / 22-d).','Seviye (Yüksek/Orta) ile süzün; ihaleye tıklayıp detaya gidin.'], ipuclari: ['Parçalama şüphesi bayrağı yalnız doğrudan temin verisi (DT worker) açıksa dolar.', ] },
|
||
kik_kararlari: { baslik: 'KİK Kurul Kararları', ne: 'İtirazen şikâyet kararlarında arama.', adimlar: ['Konu, idare veya başvuran ile arayın; benzer uyuşmazlıklarda Kurul\'un ne dediğini görün.'], ipuclari: [] },
|
||
ted_eu: { baslik: 'AB İhaleleri (TED)', ne: 'Türkiye\'deki idarelerin AB\'de yayımladığı ve Türk firmalarına açık AB ihaleleri.', adimlar: ['"Açık AB İhaleleri" sekmesiyle teklif verilebilir olanları listeleyin.'], ipuclari: [] },
|
||
planlar: { baslik: 'Planlar', ne: 'Pro ve Kurumsal planlar, yıllık indirim.', adimlar: ['Stripe ile ödeme; iptal dönem sonunda geçerli olur.'], ipuclari: [] },
|
||
api_webhooks: { baslik: 'API & Webhooks', ne: 'Kendi sistemlerinize ihale akışı.', adimlar: [
|
||
'MCP: bir API anahtarı üretin, karttaki Claude Code komutunu veya mcp.json\'u kopyalayıp anahtarı yerine yazın; asistanınız search_tenders, get_tender, search_results, search_items, upcoming_deadlines araçlarını kullanır.',
|
||
'RSS: /api/feeds/tenders.rss?key=… adresini (il, sektor, q filtreleri) Feedly / Outlook / Slack RSS\'e ekleyin; sonuç ilanları için /api/feeds/sonuc.rss anahtarsızdır.','API anahtarı üretin; webhook adresinize yeni ihaleler gönderilir.'], ipuclari: [] },
|
||
ai_ayarlari: { baslik: 'AI Model Ayarları', ne: 'Analizlerde kullanılacak model zinciri (yedekli).', adimlar: ['Sırayı sürükleyerek değiştirin; ilk başarılı model sonucu döner.', 'Gemini Flash ücretli yedek olarak zincirin sonunda her zaman denenir; seçtiğiniz modeller (ör. limitli/ücretsiz olanlar) tıkanınca analiz boş kalmaz.'], ipuclari: [] },
|
||
pwa_settings: {
|
||
baslik: 'Mobil PWA & Bildirimler',
|
||
ne: 'Uygulamayı telefona kurma, e-posta ve Telegram bildirimleri.',
|
||
adimlar: [
|
||
'Haftalık Brief (Pazartesi 08:00): bu hafta yapılacak takipli ihaleler ve geçen hafta sonuçlanan takipliler tek mesajda (Telegram + e-posta). \'Önizle\' ile şimdiki içeriği görün; anahtarla kapatın.','E-posta aboneliğinde konu/il/sektör seçin; yalnız eşleşen yeni ihaleler gelir.', 'Telegram: bota gösterilen 6 haneli kodu yazıp "Doğrula" deyin — ihale özeti, zeyilname ve sonuç bildirimleri Telegram\'a da gider.'],
|
||
ipuclari: ['Telegram için sunucuda bot tokenı tanımlı olmalı; yoksa ekran kurulum adımlarını gösterir.'],
|
||
},
|
||
smart_filter_builder: { baslik: 'Akıllı Filtre', ne: 'Kaydedilebilir çok ölçütlü filtre.', adimlar: ['Ölçütleri birleştirip kaydedin; sol menüden tek tıkla uygulanır.'], ipuclari: [] },
|
||
};
|
||
|
||
const GENEL = {
|
||
baslik: 'İhale Detayı & Analiz',
|
||
ne: 'Herhangi bir karta tıklayınca açılan detayda: ilan metni, teklif cetveli kalemleri, dokümanlar ve AI analizi.',
|
||
adimlar: ['"Tüm Evrakı Derin Analiz Et": ilan + yüklediğiniz dokümanlar birlikte okunur; elenme sebepleri, gözden kaçan ince detaylar, belge kontrol listesi, kritik tarihler ve teklife kadar yol haritası çıkar.', 'Rapor sonundaki "İhale Dosyası Hazırla": kalemleri fiyatlandırın, eksik firma bilgilerini girin; teklif mektubu, birim fiyat cetveli ve evrak kontrol listesi yazdırılabilir çıkar.', 'Profil (şirket tarama): ürünlerinizi ekleyin ya da web sitenizi taratın; uyum skoru ve "Şirketime En Uyumlu" sıralaması buna göre çalışır. Uyum eşiğini kaydırıcıdan ayarlayın.'],
|
||
ipuclari: ['Analiz yapay zekâ ürünüdür; teklif kararından önce şartnamenin aslını doğrulayın.'],
|
||
};
|
||
|
||
export const KullanimKilavuzu = () => {
|
||
const { activeTab } = useApp();
|
||
const [acik, setAcik] = useState(false);
|
||
useEffect(() => {
|
||
const h = (e) => {
|
||
if (e.key === '?' && !/input|textarea|select/i.test(e.target?.tagName || '')) setAcik((o) => !o);
|
||
if (e.key === 'Escape') setAcik(false);
|
||
};
|
||
window.addEventListener('keydown', h);
|
||
return () => window.removeEventListener('keydown', h);
|
||
}, []);
|
||
const k = KILAVUZ[activeTab] || KILAVUZ.list;
|
||
|
||
const Bolum = ({ icon: Icon, renk, baslik, satirlar, sirali }) => satirlar?.length ? (
|
||
<div className="space-y-1.5">
|
||
<h4 className="text-[11px] font-black uppercase tracking-wider text-slate-500 flex items-center gap-1.5"><Icon className={`w-3.5 h-3.5 ${renk}`} /> {baslik}</h4>
|
||
{sirali
|
||
? <ol className="list-decimal pl-5 space-y-1.5 text-xs text-slate-700 leading-relaxed">{satirlar.map((s, i) => <li key={i}>{s}</li>)}</ol>
|
||
: <ul className="pl-1 space-y-1.5 text-xs text-slate-700 leading-relaxed">{satirlar.map((s, i) => <li key={i} className="flex gap-2"><span className="text-amber-500">•</span><span>{s}</span></li>)}</ul>}
|
||
</div>
|
||
) : null;
|
||
|
||
return createPortal(
|
||
<>
|
||
<button
|
||
type="button"
|
||
onClick={() => setAcik(true)}
|
||
title="Kullanım kılavuzu (? tuşu)"
|
||
className="fixed bottom-5 right-5 z-40 w-11 h-11 rounded-full bg-slate-900 text-white shadow-lg hover:bg-blue-700 flex items-center justify-center transition-colors"
|
||
>
|
||
<HelpCircle className="w-5 h-5" />
|
||
</button>
|
||
|
||
{acik && (
|
||
<div className="fixed inset-0 z-50 flex justify-end" onClick={() => setAcik(false)}>
|
||
<div className="absolute inset-0 bg-slate-900/40" />
|
||
<aside
|
||
onClick={(e) => e.stopPropagation()}
|
||
className="relative h-full w-full max-w-md bg-white shadow-2xl flex flex-col animate-fade-in"
|
||
>
|
||
<div className="bg-slate-900 text-white px-5 py-4 flex items-center justify-between">
|
||
<div className="flex items-center gap-2">
|
||
<BookOpen className="w-5 h-5 text-amber-400" />
|
||
<div>
|
||
<p className="text-[10px] uppercase tracking-wider text-slate-400 font-bold">Kullanım kılavuzu</p>
|
||
<h3 className="font-extrabold text-sm">{k.baslik}</h3>
|
||
</div>
|
||
</div>
|
||
<button onClick={() => setAcik(false)} className="text-slate-400 hover:text-white p-1" aria-label="Kapat"><X className="w-5 h-5" /></button>
|
||
</div>
|
||
<div className="flex-1 overflow-y-auto p-5 space-y-5">
|
||
<p className="text-xs text-slate-700 leading-relaxed bg-slate-50 border border-slate-200 rounded-xl p-3">{k.ne}</p>
|
||
<Bolum icon={ListChecks} renk="text-blue-600" baslik="Nasıl kullanılır" satirlar={k.adimlar} sirali />
|
||
<Bolum icon={Lightbulb} renk="text-amber-500" baslik="İpuçları" satirlar={k.ipuclari} />
|
||
<div className="border-t border-slate-200 pt-4 space-y-3">
|
||
<h4 className="text-[11px] font-black uppercase tracking-wider text-slate-500">{GENEL.baslik}</h4>
|
||
<p className="text-xs text-slate-600 leading-relaxed">{GENEL.ne}</p>
|
||
<Bolum icon={ListChecks} renk="text-emerald-600" baslik="Detay ekranında" satirlar={GENEL.adimlar} sirali />
|
||
<Bolum icon={Lightbulb} renk="text-amber-500" baslik="Not" satirlar={GENEL.ipuclari} />
|
||
</div>
|
||
</div>
|
||
<div className="border-t border-slate-200 px-5 py-3 text-[10px] text-slate-400 flex justify-between">
|
||
<span>Her sayfada sağ alttaki ? düğmesi ya da klavyede ?</span><span>Esc kapatır</span>
|
||
</div>
|
||
</aside>
|
||
</div>
|
||
)}
|
||
</>,
|
||
document.body,
|
||
);
|
||
};
|