feat: takip izleme (zeyilname + sonuc bildirimi), hazirlik gorevi, belge sure uyarisi, Telegram, OCR, yasakli sorgu
28 depo taramasindan alinan alti ozellik. Kod kopyalanmadi (depolar lisanssiz), fikirler uygulandi. 1) Zeyilname / duzeltme ilani takibi (server/zeyilname.mjs): takipteki IKN'ler sunucuya yazilir; 3 saatte bir EKAP detayindan ilan sayisi ve ihale tarihi karsilastirilir, degisince "ne degisti" bildirimi (uygulama ici + e-posta + Telegram). Test: anlik kaydi eskitilince tur "yeni ilan yayimlandi; ihale tarihi 01.01.2026 → 08.10.2026" bildirimi uretti. 2) Sonuc bildirimi: takipteki ihale sonuclaninca "X firmasi Y'ye aldi, %Z kirim". Yalniz takiptekiler; kismi ihalede oran yazilmaz. 3) Ihaleden goreve (MOBIT-DURT): detayda "Hazirlik Gorevi Ac" → teslim tarihi ihale saati, pano "Teklif Hazirlaniyor", derin analizden gelen belge kontrol listesi gorevde; Kanban kartinda geri sayim rozeti. 4) Sirket belgesi sure takibi: Belge Kasasi'nda son kullanma tarihi girisi, dolmus / 30 gun icinde dolacak uyarisi; girise gunde bir bildirim. PATCH /api/me/documents/meta. 5) Telegram (server/telegram.mjs): TELEGRAM_BOT_TOKEN sunucuda; kullanici 6 haneli kodu bota yazar, "Dogrula" → getUpdates ile chat id baglanir. Arayuzde adim adim kurulum rehberi (BotFather → env → Redeploy → kod). Ihale ozeti, zeyilname ve sonuc bildirimleri Telegram'a da gider. 6) OCR (tesseract.js, tur): PDF metin katmani bossa (taranmis) ilk 8 sayfa OCR. Olcum: sentetik cetvelde guven 87, ~0,3 sn/sayfa; IKN ve kalemler dogru okundu. Kalem havuzu ve derin analiz artik taranmis PDF'ten de besleniyor. 7) Rakip Radari'nda KIK yasakli sorgusu baglantisi (firma adi panoya kopyalanir; EKAP sorguyu API'den vermiyor). Sinir deger hesaplayici KURULMADI: Kamu Ihale Genel Tebligi'ne ulasilamadi (mevzuat.gov.tr / ihale.gov.tr bu makineden erisilemiyor); formul kaynaksiz yazilmadi. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Send, CheckCircle2, AlertTriangle, Loader2, Unplug } from 'lucide-react';
|
||||
import { telegramDurum, telegramBagla, telegramTest, telegramSil } from '../../services/account';
|
||||
|
||||
/**
|
||||
* Telegram bildirim bağlantısı — kullanıcı kendi kurar.
|
||||
* Bot tokenı sunucuda (TELEGRAM_BOT_TOKEN); kullanıcı 6 haneli kodu bota yazar,
|
||||
* "Doğrula" deyince sunucu kodu görüp sohbeti hesaba bağlar. Token bizden geçmez.
|
||||
*/
|
||||
const kodUret = () => String(Math.floor(100000 + Math.random() * 900000));
|
||||
|
||||
export const TelegramBaglanti = () => {
|
||||
const [d, setD] = useState(null);
|
||||
const [kod] = useState(kodUret);
|
||||
const [busy, setBusy] = useState(null);
|
||||
const [mesaj, setMesaj] = useState(null);
|
||||
|
||||
const yenile = () => telegramDurum().then(setD).catch((e) => setMesaj({ tur: 'hata', metin: e.message }));
|
||||
useEffect(() => { yenile(); }, []);
|
||||
|
||||
const calistir = async (ad, fn, basari) => {
|
||||
setBusy(ad); setMesaj(null);
|
||||
try { await fn(); setMesaj({ tur: 'ok', metin: basari }); await yenile(); }
|
||||
catch (e) { setMesaj({ tur: 'hata', metin: e.message }); }
|
||||
finally { setBusy(null); }
|
||||
};
|
||||
|
||||
if (!d) return null;
|
||||
|
||||
return (
|
||||
<div className="bg-white p-6 rounded-2xl border border-slate-200 shadow-xs space-y-4">
|
||||
<div className="flex items-center justify-between border-b border-slate-100 pb-3">
|
||||
<h3 className="font-extrabold text-sm text-slate-900 flex items-center gap-2">
|
||||
<Send className="w-4 h-4 text-sky-600" /> Telegram Bildirimi
|
||||
</h3>
|
||||
{d.bagli
|
||||
? <span className="bg-emerald-100 text-emerald-800 font-extrabold text-[10px] px-2 py-0.5 rounded-full">bağlı{d.ad ? ` · ${d.ad}` : ''}</span>
|
||||
: <span className="bg-slate-100 text-slate-600 font-extrabold text-[10px] px-2 py-0.5 rounded-full">bağlı değil</span>}
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-slate-600 leading-relaxed">
|
||||
Yeni ihale özetleri, takipteki ihalede <strong>zeyilname/düzeltme ilanı</strong> ve <strong>sonuç</strong>
|
||||
("X firması Y'ye aldı, %Z kırım") e-postanın yanında Telegram'a da gelir.
|
||||
</p>
|
||||
|
||||
{!d.yapilandirilmis ? (
|
||||
<div className="bg-amber-50 border border-amber-200 rounded-xl p-4 space-y-2 text-xs text-amber-900 leading-relaxed">
|
||||
<p className="font-bold">Sunucuda Telegram botu henüz tanımlı değil. Kurulum (yönetici, bir kez):</p>
|
||||
<ol className="list-decimal pl-5 space-y-1">
|
||||
<li>Telegram'da <strong>@BotFather</strong>'a yazın: <code className="bg-amber-100 px-1 rounded">/newbot</code> → bir ad ve <code className="bg-amber-100 px-1 rounded">…bot</code> ile biten kullanıcı adı verin.</li>
|
||||
<li>BotFather'ın verdiği tokenı Coolify → ihaleist → Environment'a <code className="bg-amber-100 px-1 rounded">TELEGRAM_BOT_TOKEN</code> olarak ekleyin, Redeploy.</li>
|
||||
<li>Bu sayfa yeniden açıldığında aşağıdaki bağlama adımları görünür.</li>
|
||||
</ol>
|
||||
<p className="text-[11px] text-amber-800">Token yalnızca sunucuda durur; uygulamaya girilmez.</p>
|
||||
</div>
|
||||
) : d.bagli ? (
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<button onClick={() => calistir('test', telegramTest, 'Test mesajı gönderildi — Telegram\'ı kontrol edin.')} disabled={!!busy}
|
||||
className="bg-sky-600 hover:bg-sky-500 disabled:opacity-40 text-white font-bold text-xs px-4 py-2 rounded-lg flex items-center gap-1.5">
|
||||
{busy === 'test' ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Send className="w-3.5 h-3.5" />} Test mesajı gönder
|
||||
</button>
|
||||
<button onClick={() => calistir('sil', telegramSil, 'Telegram bağlantısı kaldırıldı.')} disabled={!!busy}
|
||||
className="bg-white hover:bg-slate-50 disabled:opacity-40 text-slate-700 font-bold text-xs px-4 py-2 rounded-lg border border-slate-300 flex items-center gap-1.5">
|
||||
<Unplug className="w-3.5 h-3.5" /> Bağlantıyı kaldır
|
||||
</button>
|
||||
</div>
|
||||
) : (
|
||||
<div className="bg-sky-50 border border-sky-200 rounded-xl p-4 space-y-3 text-xs text-slate-800">
|
||||
<p className="font-bold">Bağlamak için 3 adım:</p>
|
||||
<ol className="list-decimal pl-5 space-y-1.5 leading-relaxed">
|
||||
<li>
|
||||
Telegram'da botu açın:{' '}
|
||||
{d.bot
|
||||
? <a href={`https://t.me/${d.bot}`} target="_blank" rel="noopener noreferrer" className="font-bold text-sky-700 underline">@{d.bot}</a>
|
||||
: <span className="font-bold">(bot adı alınamadı — yöneticiye sorun)</span>}
|
||||
{' '}ve <strong>Başlat</strong>'a basın.
|
||||
</li>
|
||||
<li>Bota şu kodu yazıp gönderin: <code className="bg-white border border-sky-300 px-2 py-0.5 rounded font-mono text-sm font-black tracking-widest">{kod}</code></li>
|
||||
<li>Sonra buraya dönüp <strong>Doğrula</strong>'ya basın.</li>
|
||||
</ol>
|
||||
<button onClick={() => calistir('bagla', () => telegramBagla(kod), 'Bağlandı! Telegram\'a onay mesajı gönderildi.')} disabled={!!busy}
|
||||
className="bg-sky-600 hover:bg-sky-500 disabled:opacity-40 text-white font-bold text-xs px-5 py-2 rounded-lg flex items-center gap-1.5">
|
||||
{busy === 'bagla' ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <CheckCircle2 className="w-3.5 h-3.5" />} Doğrula
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{mesaj && (
|
||||
<div className={`rounded-lg p-3 flex gap-2 text-xs ${mesaj.tur === 'ok' ? 'bg-emerald-50 border border-emerald-200 text-emerald-900' : 'bg-red-50 border border-red-200 text-red-800'}`}>
|
||||
{mesaj.tur === 'ok' ? <CheckCircle2 className="w-4 h-4 shrink-0" /> : <AlertTriangle className="w-4 h-4 shrink-0" />}
|
||||
<span>{mesaj.metin}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user