Kullanıcı testi hataları: sabit tarihler, kişisel varsayılanlar, kılavuz düğmesi, mobil başlık, AI taslak çökmesi

- Günlük Bülten ve İhale Takvimi sabit "10.08.2026" yerine bugünden hesaplar;
  takvim 7 günü gerçek ihale tarihleriyle sayar
- Ekip/üye/firma varsayılanları oturumdaki kullanıcıdan; sahte "Ekip Notları"
  ve sabit "%94 Uyum" rozeti kaldırıldı (gerçek uyum skoru varsa gösterilir)
- Kılavuz düğmesi document.body'ye portal (ekran dışına düşüyordu)
- Detay modalı havuzdaki güncel kaydı okur: Takip Et / Görev aç etiketleri güncellenir
- Mobil: başlıkta ikincil düğmeler md altında gizli, arama formu küçülebilir
- PWA sayfası: sahte önbellek sayıları yerine Cache API ve bellek sayımı
- Sözleşme sorgula pazar payı bir ondalık (hep %0 görünüyordu)
- Rakip Radar "SMS" vaadi düzeltildi; sektör=CPV etiketi tekrarı gizlendi;
  Stripe kurulum uyarısı yalnız yöneticiye
- Açılışta 7 gereksiz PUT /api/me/data: ilk yükleme yazımı atlanır
- server: teklif taslağı çökmesi (kalemOku nesne döndürür), denetim yanıtından
  model adı kaldırıldı, gereklilikler satır bazlı; derin analiz 12k token +
  kesik JSON onarımı

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
2026-09-05 09:43:41 +03:00
co-authored by Claude Fable 5.1
parent 6d7d938711
commit 08c0a0af7e
14 changed files with 130 additions and 60 deletions
+24 -15
View File
@@ -1,4 +1,4 @@
import React, {createContext, useContext, useState, useEffect, useMemo } from 'react';
import React, {createContext, useContext, useState, useEffect, useMemo, useRef } from 'react';
import { parseWebsiteCapabilitiesDynamically } from '../services/webScraperService';
import { havuzuPuanla, profilMetni, profilTerimleri } from '../services/uyum';
import { getToken } from '../services/auth';
@@ -168,6 +168,15 @@ export const AppProvider = ({ children }) => {
// localStorage yalnızca çevrimdışı önbellek; kaynak doğruluk sunucuda.
const [plan, setPlan] = useState(null);
const [veriSenkron, setVeriSenkron] = useState(false);
// Sunucu verisi yüklendiğinde her effect bir kez tetiklenir; bu ilk turda
// yazmak gereksiz (aynı veri) ve boş istemci state'iyle ezme riski taşır.
const ilkYazmaAtlanan = useRef(new Set());
// İlk tetiklenme (sunucu verisi yüklenirken) yazılmaz; sonrakiler yazılır.
const ilkYazmaDegil = (anahtar, deger) => {
if (ilkYazmaAtlanan.current.has(anahtar)) { sunucuyaYaz(anahtar, deger); return; }
ilkYazmaAtlanan.current.add(anahtar);
};
const sunucuyaYaz = (anahtar, deger) => {
if (!authUser) return;
@@ -253,7 +262,7 @@ export const AppProvider = ({ children }) => {
});
useEffect(() => {
yerelKaydet('ihaleist_rakip_izleme', competitorWatchlist);
if (veriSenkron) sunucuyaYaz('rakip_izleme', competitorWatchlist);
if (veriSenkron) ilkYazmaDegil('rakip_izleme', competitorWatchlist);
}, [competitorWatchlist, veriSenkron]);
// --- Favori kurumlar (localStorage, gerçek) ---
@@ -262,7 +271,7 @@ export const AppProvider = ({ children }) => {
});
useEffect(() => {
yerelKaydet('ihaleist_favori_kurumlar', favoriteKurumlar);
if (veriSenkron) sunucuyaYaz('favori_kurumlar', favoriteKurumlar);
if (veriSenkron) ilkYazmaDegil('favori_kurumlar', favoriteKurumlar);
}, [favoriteKurumlar, veriSenkron]);
const toggleFavoriteKurum = (kurum) => {
const ad = (kurum || '').trim();
@@ -276,13 +285,12 @@ export const AppProvider = ({ children }) => {
const saved = localStorage.getItem('ihaleist_ekip');
if (saved) return JSON.parse(saved);
} catch { /* yoksay */ }
return [
{ id: 'tm-1', name: 'Hikmet Tanrıverdi', email: 'hikmet044@gmail.com', role: 'Yönetici' },
];
const u = getStoredUser();
return u ? [{ id: 'tm-1', name: u.name || u.email, email: u.email || '', role: 'Yönetici' }] : [];
});
useEffect(() => {
yerelKaydet('ihaleist_ekip', teamMembers);
if (veriSenkron) sunucuyaYaz('ekip', teamMembers);
if (veriSenkron) ilkYazmaDegil('ekip', teamMembers);
}, [teamMembers, veriSenkron]);
const addTeamMember = (m) => setTeamMembers((prev) => [...prev, { id: 'tm-' + Date.now(), ...m }]);
const removeTeamMember = (id) => setTeamMembers((prev) => prev.filter((m) => m.id !== id));
@@ -309,7 +317,7 @@ export const AppProvider = ({ children }) => {
});
useEffect(() => {
yerelKaydet('ihaleist_reminder', reminderSettings);
if (veriSenkron) sunucuyaYaz('reminder', reminderSettings);
if (veriSenkron) ilkYazmaDegil('reminder', reminderSettings);
}, [reminderSettings, veriSenkron]);
// Şu an sessiz saat aralığında mıyız (gece yarısını geçen aralıkları da destekler)
@@ -416,11 +424,12 @@ export const AppProvider = ({ children }) => {
// Yetkinlikler uyum skorunun girdisi — sayfa yenilenince sıfırlanmasın
let kayitli = null;
try { kayitli = JSON.parse(localStorage.getItem('ihaleist_firma_profil') || 'null'); } catch { /* yoksay */ }
const u = getStoredUser();
return {
name: 'Hikmet Tanrıverdi',
username: 'tanriverdi',
company: kayitli?.company || 'Tanrıverdi Medikal & Sağlık Teknolojileri A.Ş.',
website: kayitli?.website || 'https://www.tanriverdimedikal.com.tr',
name: u?.name || u?.email || 'Kullanıcı',
username: (u?.email || '').split('@')[0] || 'kullanici',
company: kayitli?.company || '',
website: kayitli?.website || '',
trialDaysLeft: 7,
role: 'Yönetici Tedarikçi',
scrapedCapabilities: { ...BOS_YETKINLIK, ...(kayitli?.scrapedCapabilities || {}) },
@@ -434,7 +443,7 @@ export const AppProvider = ({ children }) => {
scrapedCapabilities: userProfile.scrapedCapabilities, uyumEsigi: userProfile.uyumEsigi,
};
yerelKaydet('ihaleist_firma_profil', veri);
if (veriSenkron) sunucuyaYaz('firma_profil', veri);
if (veriSenkron) ilkYazmaDegil('firma_profil', veri);
}, [userProfile.company, userProfile.website, userProfile.scrapedCapabilities, userProfile.uyumEsigi, veriSenkron]);
const setUyumEsigi = (v) => setUserProfile((p) => ({ ...p, uyumEsigi: Math.max(0, Math.min(100, Number(v) || 0)) }));
@@ -548,7 +557,7 @@ export const AppProvider = ({ children }) => {
useEffect(() => {
yerelKaydet('ihaleist_saved_filters', savedFilters);
if (veriSenkron) sunucuyaYaz('saved_filters', savedFilters);
if (veriSenkron) ilkYazmaDegil('saved_filters', savedFilters);
}, [savedFilters, veriSenkron]);
// (Sahte "online kullanıcı" rastgele sayacı kaldırıldı — canlı istatistikler
@@ -571,7 +580,7 @@ export const AppProvider = ({ children }) => {
);
const takipImza = takipIknler.join('|');
useEffect(() => {
if (veriSenkron) sunucuyaYaz('takip', takipIknler);
if (veriSenkron) ilkYazmaDegil('takip', takipIknler);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [takipImza, veriSenkron]);