From 351884ac0292b62111c109d62f5bace65e3da31e Mon Sep 17 00:00:00 2001 From: ht Date: Sat, 29 Aug 2026 14:43:20 +0300 Subject: [PATCH] =?UTF-8?q?fix:=2012=20bin=20ihalelik=20havuz=20localStora?= =?UTF-8?q?ge=20kotas=C4=B1n=C4=B1=20a=C5=9F=C4=B1p=20uygulamay=C4=B1=20be?= =?UTF-8?q?yaz=20ekranla=20=C3=A7=C3=B6kertiyordu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/context/AppContext.jsx | 65 +++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/src/context/AppContext.jsx b/src/context/AppContext.jsx index 6157f00..8d30ce1 100644 --- a/src/context/AppContext.jsx +++ b/src/context/AppContext.jsx @@ -6,11 +6,44 @@ import { getAllUserData, setUserData, getPlan } from '../services/account'; const AppContext = createContext(); +// İhale önbelleği: tam havuz (10.000+ kayıt) localStorage kotasına sığmaz. +const TENDER_CACHE_KEY = 'ihaleist_tenders'; +const TENDER_CACHE_LIMIT = 300; + +// localStorage her zaman yazılabilir değildir (kota dolu, gizli sekme, site +// verisi kapalı). Yazma hatası uygulamayı çökertmemeli. +const yerelKaydet = (anahtar, deger) => { + try { + localStorage.setItem(anahtar, JSON.stringify(deger)); + return true; + } catch { + return false; + } +}; +const yerelOku = (anahtar, varsayilan) => { + try { + const ham = localStorage.getItem(anahtar); + if (!ham) return varsayilan; + const v = JSON.parse(ham); + return v == null ? varsayilan : v; + } catch { + return varsayilan; + } +}; + export const AppProvider = ({ children }) => { const [activeTab, setActiveTab] = useState('list'); // 'list', 'bulten', 'sonuc', 'takvim', 'sozlesme', 'kdv', 'fiyat', 'kanban', 'smart_filter_builder' const [tenders, setTenders] = useState(() => { - const saved = localStorage.getItem('ihaleist_tenders'); - return saved ? JSON.parse(saved) : []; + // Sadece ilk boyama için küçük bir önbellek; asıl kaynak sunucudur. + // Bozuk/eski kayıt uygulamayı çökertmemeli. + try { + const saved = localStorage.getItem(TENDER_CACHE_KEY); + const parsed = saved ? JSON.parse(saved) : null; + return Array.isArray(parsed) ? parsed : []; + } catch { + try { localStorage.removeItem(TENDER_CACHE_KEY); } catch { /* yok say */ } + return []; + } }); const [liveStats, setLiveStats] = useState({ toplamIhale: 0, bugunEklenen: 0, arsiv: 0 }); @@ -23,8 +56,8 @@ export const AppProvider = ({ children }) => { const [quickFilter, setQuickFilter] = useState('all'); const [savedFilters, setSavedFilters] = useState(() => { - const saved = localStorage.getItem('ihaleist_saved_filters'); - return saved ? JSON.parse(saved) : []; + const v = yerelOku('ihaleist_saved_filters', []); + return Array.isArray(v) ? v : []; }); const [isLiveMode, setIsLiveMode] = useState(true); @@ -142,7 +175,7 @@ export const AppProvider = ({ children }) => { try { return JSON.parse(localStorage.getItem('ihaleist_rakip_izleme') || '[]'); } catch { return []; } }); useEffect(() => { - localStorage.setItem('ihaleist_rakip_izleme', JSON.stringify(competitorWatchlist)); + yerelKaydet('ihaleist_rakip_izleme', competitorWatchlist); if (veriSenkron) sunucuyaYaz('rakip_izleme', competitorWatchlist); }, [competitorWatchlist, veriSenkron]); @@ -151,7 +184,7 @@ export const AppProvider = ({ children }) => { try { return JSON.parse(localStorage.getItem('ihaleist_favori_kurumlar') || '[]'); } catch { return []; } }); useEffect(() => { - localStorage.setItem('ihaleist_favori_kurumlar', JSON.stringify(favoriteKurumlar)); + yerelKaydet('ihaleist_favori_kurumlar', favoriteKurumlar); if (veriSenkron) sunucuyaYaz('favori_kurumlar', favoriteKurumlar); }, [favoriteKurumlar, veriSenkron]); const toggleFavoriteKurum = (kurum) => { @@ -171,7 +204,7 @@ export const AppProvider = ({ children }) => { ]; }); useEffect(() => { - localStorage.setItem('ihaleist_ekip', JSON.stringify(teamMembers)); + yerelKaydet('ihaleist_ekip', teamMembers); if (veriSenkron) sunucuyaYaz('ekip', teamMembers); }, [teamMembers, veriSenkron]); const addTeamMember = (m) => setTeamMembers((prev) => [...prev, { id: 'tm-' + Date.now(), ...m }]); @@ -182,7 +215,7 @@ export const AppProvider = ({ children }) => { try { return JSON.parse(localStorage.getItem('ihaleist_belgeler') || '[]'); } catch { return []; } }); useEffect(() => { - localStorage.setItem('ihaleist_belgeler', JSON.stringify(vaultDocuments)); + yerelKaydet('ihaleist_belgeler', vaultDocuments); }, [vaultDocuments]); const [reminderSettings, setReminderSettings] = useState(() => { @@ -198,7 +231,7 @@ export const AppProvider = ({ children }) => { } catch { return def; } }); useEffect(() => { - localStorage.setItem('ihaleist_reminder', JSON.stringify(reminderSettings)); + yerelKaydet('ihaleist_reminder', reminderSettings); if (veriSenkron) sunucuyaYaz('reminder', reminderSettings); }, [reminderSettings, veriSenkron]); @@ -266,7 +299,7 @@ export const AppProvider = ({ children }) => { }); useEffect(() => { - localStorage.setItem('ihaleist_ai_model_config', JSON.stringify(aiModelConfig)); + yerelKaydet('ihaleist_ai_model_config', aiModelConfig); }, [aiModelConfig]); const getModelById = (id) => AI_MODEL_CATALOG.find(m => m.id === id); @@ -353,12 +386,20 @@ export const AppProvider = ({ children }) => { const markNotificationsRead = () => setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); // Persist state to localstorage + // Havuz on binlerce kayda çıkabiliyor; tamamı localStorage kotasını (≈5 MB) + // aşar ve yakalanmazsa uygulama beyaz ekranla çöker. Bu yüzden yalnızca ilk + // boyamaya yetecek kadarını, hataya dayanıklı biçimde saklıyoruz. useEffect(() => { - localStorage.setItem('ihaleist_tenders', JSON.stringify(tenders)); + try { + localStorage.setItem(TENDER_CACHE_KEY, JSON.stringify(tenders.slice(0, TENDER_CACHE_LIMIT))); + } catch { + // Kota dolduysa önbelleği bırak — veri zaten sunucudan geliyor. + try { localStorage.removeItem(TENDER_CACHE_KEY); } catch { /* yok say */ } + } }, [tenders]); useEffect(() => { - localStorage.setItem('ihaleist_saved_filters', JSON.stringify(savedFilters)); + yerelKaydet('ihaleist_saved_filters', savedFilters); if (veriSenkron) sunucuyaYaz('saved_filters', savedFilters); }, [savedFilters, veriSenkron]);