feat: her sayfada sagdan acilan kullanim kilavuzu (? dugmesi / ? tusu), sekmeye gore icerik

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-04 00:36:55 +03:00
co-authored by Claude Fable 5.1
parent 91b8ea94b8
commit acec88a4c4
2 changed files with 142 additions and 0 deletions
+2
View File
@@ -22,6 +22,7 @@ import { TenderCalendar } from './components/tools/TenderCalendar';
import { ContractSearch } from './components/tools/ContractSearch'; import { ContractSearch } from './components/tools/ContractSearch';
import { VatCalculator } from './components/tools/VatCalculator'; import { VatCalculator } from './components/tools/VatCalculator';
import { SinirDeger } from './components/tools/SinirDeger'; import { SinirDeger } from './components/tools/SinirDeger';
import { KullanimKilavuzu } from './components/KullanimKilavuzu';
import { PriceAnalysis } from './components/tools/PriceAnalysis'; import { PriceAnalysis } from './components/tools/PriceAnalysis';
import { CompetitorRadar } from './components/tools/CompetitorRadar'; import { CompetitorRadar } from './components/tools/CompetitorRadar';
import { DocumentVault } from './components/tools/DocumentVault'; import { DocumentVault } from './components/tools/DocumentVault';
@@ -82,6 +83,7 @@ const MainContent = () => {
<CompanyScannerModal /> <CompanyScannerModal />
<AiSpecAnalyzerModal /> <AiSpecAnalyzerModal />
<UrunAramaModal /> <UrunAramaModal />
<KullanimKilavuzu />
</main> </main>
); );
}; };
+140
View File
@@ -0,0 +1,140 @@
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: [
'Ü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: ['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: ['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: ['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: ['Seviye (Yüksek/Orta) ile süzün; ihaleye tıklayıp detaya gidin.'], ipuclari: [] },
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: ['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.'], ipuclari: [] },
pwa_settings: {
baslik: 'Mobil PWA & Bildirimler',
ne: 'Uygulamayı telefona kurma, e-posta ve Telegram bildirimleri.',
adimlar: ['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 (
<>
<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>
)}
</>
);
};