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ı)
This commit is contained in:
2026-08-28 01:58:46 +03:00
parent 512d529af2
commit 0ab74143c0
16 changed files with 895 additions and 154 deletions
+58 -17
View File
@@ -1,13 +1,24 @@
import React, { useState } from 'react';
import { useApp } from '../../context/AppContext';
import { Smartphone, Wifi, WifiOff, Download, Bell, CheckCircle2, ShieldCheck, Zap, Sparkles } from 'lucide-react';
import { Smartphone, Wifi, WifiOff, Download, Bell, CheckCircle2, ShieldCheck, Zap, Sparkles, Moon } from 'lucide-react';
import confetti from 'canvas-confetti';
export const PwaSettings = () => {
const { userProfile } = useApp();
const { userProfile, reminderSettings, setReminderSettings, requestPushPermission, notify, isQuietNow } = useApp();
const [offlineMode, setOfflineMode] = useState(true);
const [pushSubscribed, setPushSubscribed] = 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);
@@ -67,30 +78,60 @@ export const PwaSettings = () => {
</button>
</div>
{/* Mobile Push Notifications */}
{/* 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 Mobil Push Bildirimleri
<Bell className="w-4 h-4 text-amber-500" /> Anlık Bildirimler
</h3>
<span className="bg-amber-100 text-amber-800 font-extrabold text-[10px] px-2 py-0.5 rounded-full">
İzin Verildi
<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">
İhale son başvuru saatine 3 saat kala mobil cihazınıza sesli ve titreşimli anlık uyarı bildirimi gönderilir.
Tarayıcı bildirimlerine izin verin; yeni ihale ve son tarih uyarıları gerçek bildirim olarak gelir.
</p>
<label className="flex items-center justify-between p-3 bg-slate-50 rounded-xl border border-slate-200 cursor-pointer text-xs font-bold text-slate-800">
<span>Mobil Push Uyarısı</span>
<input
type="checkbox"
checked={pushSubscribed}
onChange={() => setPushSubscribed(!pushSubscribed)}
className="w-4 h-4 accent-cyan-600 rounded"
/>
</label>
{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 */}