Files
ihaleist/src/components/tools/PwaSettings.jsx
T
hikmettan 0ab74143c0 feat: demo özelliklerini gerçekleştir — canlı veri + localStorage
- analytics.js: firmStats/itemStats/provinceDensity (gerçek hesaplama)
- Rakip Radar: canlı veriden kazanan firma analizi (mock 3 firma kaldırıldı)
- Fiyat Analizi: gerçek kalem min/medyan/ort/maks + CSV export
- TurkeyHeatMap: 81 il gerçek interaktif yoğunluk haritası (tıkla→filtrele)
- Favori kurumlar: kart yıldızı + hızlı filtre (localStorage)
- Ekip yönetimi: gerçek CRUD (RightSidebar) + Kanban ataması ekipten
- Bildirimler: gerçek Notification API + izin + test + sessiz saatler
- PDF: jsPDF ile gerçek birim fiyat teklif cetveli (kaşe/imza alanı)
2026-08-28 01:58:46 +03:00

176 lines
8.6 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { useApp } from '../../context/AppContext';
import { Smartphone, Wifi, WifiOff, Download, Bell, CheckCircle2, ShieldCheck, Zap, Sparkles, Moon } from 'lucide-react';
import confetti from 'canvas-confetti';
export const PwaSettings = () => {
const { userProfile, reminderSettings, setReminderSettings, requestPushPermission, notify, isQuietNow } = useApp();
const [offlineMode, setOfflineMode] = useState(true);
const [installed, setInstalled] = useState(false);
const [permission, setPermission] = useState(typeof Notification !== 'undefined' ? Notification.permission : 'unsupported');
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 }));
const handleInstallPwa = () => {
setInstalled(true);
confetti({ particleCount: 70 });
};
return (
<div className="space-y-6 animate-fade-in">
{/* Header Banner */}
<div className="bg-linear-to-r from-slate-900 via-blue-950 to-cyan-900 text-white p-6 rounded-2xl shadow-lg border border-cyan-800/40 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-2xl bg-cyan-600 text-white flex items-center justify-center font-black shadow-md">
<Smartphone className="w-6 h-6" />
</div>
<div>
<h2 className="font-extrabold text-xl">Mobil PWA & Saha Ekibi Offline Modu</h2>
<p className="text-xs text-cyan-200 mt-0.5">Saha satış temsilcileriniz için internet bağlantısı olmadığında dahi çevrimdışı çalışma ve anlık push bildirimi.</p>
</div>
</div>
</div>
{/* Main Controls Grid */}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{/* Offline Cache Status */}
<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">
{offlineMode ? <Wifi className="w-4 h-4 text-emerald-600" /> : <WifiOff className="w-4 h-4 text-slate-400" />} Çevrimdışı (Offline) Önbellek
</h3>
<span className="bg-emerald-100 text-emerald-800 font-extrabold text-[10px] px-2 py-0.5 rounded-full">
Senkronize
</span>
</div>
<div className="space-y-2 text-xs text-slate-700">
<p className="flex justify-between">
<span>İhale Kayıtları:</span>
<strong className="text-slate-900">102 İhale Önbellekte</strong>
</p>
<p className="flex justify-between">
<span>Şartname PDF'leri:</span>
<strong className="text-slate-900">18 Belge Çevrimdışı</strong>
</p>
<p className="flex justify-between">
<span>Son Senkronizasyon:</span>
<strong className="text-emerald-700">Bugün 14:45</strong>
</p>
</div>
<button
onClick={() => setOfflineMode(!offlineMode)}
className="w-full bg-slate-100 hover:bg-slate-200 text-slate-800 font-bold text-xs py-2.5 rounded-xl border border-slate-200 transition-colors"
>
{offlineMode ? 'Çevrimdışı Önbelleği Yenile' : 'Çevrimdışı Modu Aktif Et'}
</button>
</div>
{/* Gerçek Tarayıcı Bildirimleri + Sessiz Saatler */}
<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">
<Bell className="w-4 h-4 text-amber-500" /> Anlık Bildirimler
</h3>
<span className={`font-extrabold text-[10px] px-2 py-0.5 rounded-full ${
permission === 'granted' ? 'bg-emerald-100 text-emerald-800' : permission === 'denied' ? 'bg-red-100 text-red-700' : 'bg-slate-100 text-slate-600'
}`}>
{permission === 'granted' ? 'İzin Verildi' : permission === 'denied' ? 'Reddedildi' : permission === 'unsupported' ? 'Desteklenmiyor' : 'İzin Bekliyor'}
</span>
</div>
<p className="text-xs text-slate-600 leading-relaxed">
Tarayıcı bildirimlerine izin verin; yeni ihale ve son tarih uyarıları gerçek bildirim olarak gelir.
</p>
{permission !== 'granted' ? (
<button onClick={handleEnablePush} disabled={permission === 'unsupported'}
className="w-full bg-amber-500 hover:bg-amber-400 disabled:opacity-40 text-white font-bold text-xs py-2.5 rounded-xl">
Bildirimlere İzin Ver
</button>
) : (
<button onClick={() => notify('Test bildirimi', isQuietNow() ? 'Sessiz saatte — sadece listeye eklendi.' : 'Bildirimler çalışıyor ✓')}
className="w-full bg-emerald-600 hover:bg-emerald-500 text-white font-bold text-xs py-2.5 rounded-xl">
Test Bildirimi Gönder
</button>
)}
{/* Sessiz saatler — gerçek */}
<div className="pt-2 border-t border-slate-100 space-y-2">
<label className="flex items-center justify-between text-xs font-bold text-slate-800 cursor-pointer">
<span className="flex items-center gap-1.5"><Moon className="w-3.5 h-3.5 text-indigo-500" /> Sessiz Saatler</span>
<input type="checkbox" checked={reminderSettings.quietEnabled}
onChange={(e) => setQuiet({ quietEnabled: e.target.checked })}
className="w-4 h-4 accent-indigo-600 rounded" />
</label>
{reminderSettings.quietEnabled && (
<div className="flex items-center gap-2 text-xs">
<input type="time" value={reminderSettings.quietStart}
onChange={(e) => setQuiet({ quietStart: e.target.value })}
className="bg-slate-50 border border-slate-300 rounded-lg px-2 py-1.5 flex-1" />
<span className="text-slate-400">→</span>
<input type="time" value={reminderSettings.quietEnd}
onChange={(e) => setQuiet({ quietEnd: e.target.value })}
className="bg-slate-50 border border-slate-300 rounded-lg px-2 py-1.5 flex-1" />
</div>
)}
{reminderSettings.quietEnabled && (
<p className="text-[10px] text-slate-400">
{isQuietNow() ? '🔕 Şu an sessiz saatte — tarayıcı bildirimi gösterilmez, uyarılar listeye eklenir.' : '🔔 Şu an aktif — bildirimler gösterilir.'}
</p>
)}
</div>
</div>
{/* Install PWA Application */}
<div className="bg-linear-to-br from-slate-900 to-cyan-950 text-white p-6 rounded-2xl shadow-xs space-y-4 border border-cyan-800/40">
<div className="flex items-center justify-between">
<h3 className="font-black text-sm text-white flex items-center gap-2">
<Download className="w-4 h-4 text-cyan-400" /> İhaleist Mobil / Masaüstü
</h3>
<span className="bg-cyan-500 text-slate-950 font-black text-[10px] px-2 py-0.5 rounded-full">
PWA 2.0
</span>
</div>
<p className="text-xs text-slate-300 leading-relaxed">
İhaleist'i iOS, Android, Windows veya Mac cihazınıza bir tıklama ile uygulama olarak kurun.
</p>
<button
onClick={handleInstallPwa}
disabled={installed}
className="w-full bg-cyan-500 hover:bg-cyan-400 disabled:bg-slate-700 text-slate-950 font-black text-xs py-3 rounded-xl flex items-center justify-center gap-2 shadow-md transition-colors cursor-pointer"
>
{installed ? (
<>
<CheckCircle2 className="w-4 h-4 text-emerald-950" />
<span>Cihaza Yüklendi!</span>
</>
) : (
<>
<Download className="w-4 h-4 text-slate-950" />
<span>İhaleist Uygulamasını Cihaza Yükle</span>
</>
)}
</button>
</div>
</div>
</div>
);
};