import {DeliveryQueue} from './DeliveryQueue'; import React, { useState, useEffect } from 'react'; import { useApp } from '../../context/AppContext'; import { Smartphone, Wifi, WifiOff, Download, Bell, CheckCircle2, Moon, Mail, Send } from 'lucide-react'; import confetti from 'canvas-confetti'; import { getToken } from '../../services/auth'; import { NotifySubscriptions } from './NotifySubscriptions'; export const PwaSettings = () => { const { userProfile, authUser, tenders, reminderSettings, setReminderSettings, requestPushPermission, notify, isQuietNow } = useApp(); // Gerçek Cache API sayımı — sahte "102 ihale önbellekte" yerine const [onbellekSayisi, setOnbellekSayisi] = useState(null); const [onbellekAdi, setOnbellekAdi] = useState(''); const [cacheLoading, setCacheLoading] = useState(false); const [cacheError, setCacheError] = useState(null); const [online, setOnline] = useState(navigator.onLine); const refreshCacheStatus = async () => { setCacheLoading(true); setCacheError(null); try { if (!('caches' in window)) throw new Error('Bu tarayıcı uygulama önbelleğini desteklemiyor.'); const adlar = (await caches.keys()).filter(name => name.startsWith('ihaleist-')); let toplam = 0; for (const ad of adlar) toplam += (await (await caches.open(ad)).keys()).length; setOnbellekSayisi(toplam); setOnbellekAdi(adlar.join(', ')); } catch (error) { setCacheError(error.message); setOnbellekSayisi(null); } finally { setCacheLoading(false); } }; useEffect(() => { refreshCacheStatus(); }, []); useEffect(() => { const changed = () => setOnline(navigator.onLine); window.addEventListener('online', changed); window.addEventListener('offline', changed); return () => { window.removeEventListener('online', changed); window.removeEventListener('offline', changed); }; }, []); const [installed, setInstalled] = useState(false); const [permission, setPermission] = useState(typeof Notification !== 'undefined' ? Notification.permission : 'unsupported'); // E-posta (Resend) durumu + test gönderimi const [emailCfg, setEmailCfg] = useState(null); // null=bilinmiyor, true/false const [emailSending, setEmailSending] = useState(false); const [emailResult, setEmailResult] = useState(null); useEffect(() => { fetch('/api/notify/email/config') .then((r) => r.json()) .then((d) => setEmailCfg(Boolean(d.configured))) .catch(() => setEmailCfg(false)); }, []); const sendTestEmail = async () => { setEmailSending(true); setEmailResult(null); try { const yeniler = tenders.filter((t) => t.isYeni).slice(0, 15); const res = await fetch('/api/notify/email/test', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${getToken()}` }, body: JSON.stringify({ subject: 'İhaleist — bildirim testi', tenders: yeniler }), }); const data = await res.json(); if (data.success) { setEmailResult({ ok: true, msg: `E-posta gönderildi → ${data.to}` }); notify('E-posta gönderildi', `Test e-postası ${data.to} adresine iletildi.`); } else { setEmailResult({ ok: false, msg: data.error || 'Gönderilemedi' }); } } catch (e) { setEmailResult({ ok: false, msg: e.message }); } finally { setEmailSending(false); } }; const handleEnablePush = async () => { const p = await requestPushPermission(); setPermission(p); if (p === 'granted') { setReminderSettings((s) => ({ ...s, pushEnabled: true })); notify('Bildirimler açıldı', 'Yeni ihale ve son tarih uyarıları buradan gelecek.'); confetti({ particleCount: 50 }); } }; const setQuiet = (patch) => setReminderSettings((s) => ({ ...s, ...patch })); // Gerçek PWA kurulumu: tarayıcı beforeinstallprompt verirse yerel diyalog; // zaten standalone açıldıysa "yüklü"; iOS Safari'de olay yok → yol tarifi. const [kurulumOlayi, setKurulumOlayi] = useState(null); const [iosIpucu, setIosIpucu] = useState(false); useEffect(() => { const standalone = window.matchMedia?.('(display-mode: standalone)')?.matches || window.navigator.standalone === true; if (standalone) setInstalled(true); const yakala = (e) => { e.preventDefault(); setKurulumOlayi(e); }; const kuruldu = () => { setInstalled(true); setKurulumOlayi(null); }; window.addEventListener('beforeinstallprompt', yakala); window.addEventListener('appinstalled', kuruldu); return () => { window.removeEventListener('beforeinstallprompt', yakala); window.removeEventListener('appinstalled', kuruldu); }; }, []); const handleInstallPwa = async () => { if (kurulumOlayi) { kurulumOlayi.prompt(); const { outcome } = await kurulumOlayi.userChoice.catch(() => ({ outcome: 'dismissed' })); if (outcome === 'accepted') setInstalled(true); setKurulumOlayi(null); return; } setIosIpucu(true); // olay yoksa (iOS / zaten yüklü / desteklenmiyor) yol tarifi göster }; return (
{/* Header Banner */}

Mobil uygulama ve bağlantı durumu

Uygulama kabuğu önbellekten açılabilir. İhale güncelleme, belge erişimi ve hazırlık kaydı için internet bağlantısı gerekir.

{/* Main Controls Grid */}
{/* Offline Cache Status */}

{online ? : } Çevrimdışı (Offline) Önbellek

{online ? 'Bağlantı var' : 'Çevrimdışı'}

İhale Kayıtları (bellekte): {tenders.length.toLocaleString('tr-TR')} ihale

Önbelleğe alınmış istek: {onbellekSayisi == null ? '—' : `${onbellekSayisi} kayıt`}

Uygulama sürümü önbelleği: {onbellekAdi || '—'}

{cacheError &&

{cacheError}

}

Bağlantı durumu sunucuya erişimi garanti etmez. Çevrimdışı değişiklikler otomatik gönderim kuyruğuna alınmaz; kayıt sonucunu kontrol edin.

{/* Gerçek Tarayıcı Bildirimleri + Sessiz Saatler */}

Anlık Bildirimler

{permission === 'granted' ? 'İzin Verildi' : permission === 'denied' ? 'Reddedildi' : permission === 'unsupported' ? 'Desteklenmiyor' : 'İzin Bekliyor'}

Tarayıcı bildirimlerine izin verin; yeni ihale ve son tarih uyarıları gerçek bildirim olarak gelir.

{permission !== 'granted' ? ( ) : ( )} {/* Sessiz saatler — gerçek */}
{reminderSettings.quietEnabled && (
setQuiet({ quietStart: e.target.value })} className="bg-slate-50 border border-slate-300 rounded-lg px-2 py-1.5 flex-1" /> → setQuiet({ quietEnd: e.target.value })} className="bg-slate-50 border border-slate-300 rounded-lg px-2 py-1.5 flex-1" />
)} {reminderSettings.quietEnabled && (

{isQuietNow() ? '🔕 Şu an sessiz saatte — tarayıcı bildirimi gösterilmez, uyarılar listeye eklenir.' : '🔔 Şu an aktif — bildirimler gösterilir.'}

)}
{/* E-posta Bildirimi (Resend) — gerçek gönderim */}

E-posta Bildirimi

{emailCfg === true ? 'Aktif (Resend)' : emailCfg === false ? 'Yapılandırılmadı' : 'Kontrol ediliyor…'}

{emailCfg === false ? 'E-posta göndermek için sunucuya RESEND_API_KEY ekleyin (Coolify → Environment). Ayrıca gönderen domainini Resend’de DNS ile doğrulayın.' : 'Takip kriterlerinize uyan ihaleler, hesabınızın e-posta adresine özet olarak gönderilir. Aşağıdan kendinize test gönderebilirsiniz.'}

{emailResult && (

{emailResult.ok ? '✓ ' : '⚠ '}{emailResult.msg}

)}
{/* Install PWA Application */}

İhaleist Mobil / Masaüstü

PWA 2.0

İhaleist'i iOS, Android, Windows veya Mac cihazınıza bir tıklama ile uygulama olarak kurun.

{iosIpucu && !installed && (

iPhone / iPad (Safari): alttaki Paylaş simgesi → Ana Ekrana Ekle.

Android (Chrome): sağ üst ⋮ menü → Uygulamayı yükle / Ana ekrana ekle.

Masaüstü (Chrome/Edge): adres çubuğunun sağındaki yükleme simgesi.

)}
{/* Otomatik bildirim abonelikleri */}
); };