@@ -5,7 +5,7 @@ import { getToken } from '../services/auth';
import { fetchLiveTenders , fetchTenderDetail , mergeUserState } from '../services/tenderApi' ;
import { fetchLiveTenders , fetchTenderDetail , mergeUserState } from '../services/tenderApi' ;
import { restoreSession , clearSession , getStoredUser } from '../services/auth' ;
import { restoreSession , clearSession , getStoredUser } from '../services/auth' ;
import { getAllUserData , setUserData , getPlan , bildirimleriGetir , bildirimOkundu , listDocuments } from '../services/account' ;
import { getAllUserData , setUserData , getPlan , bildirimleriGetir , bildirimOkundu , listDocuments } from '../services/account' ;
import { applyTenderStates , tenderStateKey , tenderSnapshot } from '../services/tenderState' ;
import { applyTenderStates , tenderStateKey , tenderSnapshot , publicTender } from '../services/tenderState' ;
import { getTenderStates , saveTenderState } from '../services/account' ;
import { getTenderStates , saveTenderState } from '../services/account' ;
import { SECTORS _LIST , INSTITUTION _TYPES _LIST } from '../data/constants' ;
import { SECTORS _LIST , INSTITUTION _TYPES _LIST } from '../data/constants' ;
@@ -25,16 +25,13 @@ const yerelKaydet = (anahtar, deger) => {
return false ;
return false ;
}
}
} ;
} ;
const yerelOku = ( anahtar , varsayilan ) => {
const defaultReminder = ( ) => ( { emailEnabled : true , smsEnabled : true , pushEnabled : true ,
try {
notify3DaysBefore : true , notify1DayBefore : true , notify3HoursBefore : true ,
const ham = localStorage . getItem ( anahtar ) ;
googleCalendarSync : true , outlookSync : false , quietEnabled : false , quietStart : '22:00' , quietEnd : '08:00' } ) ;
if ( ! ham ) return varsayilan ;
const defaultProfile = user => ( { name : user ? . name || user ? . email || 'Kullanı cı ' ,
const v = JSON . parse ( ham ) ;
username : ( user ? . email || '' ) . split ( '@' ) [ 0 ] || 'kullanici' , company : '' , website : '' , trialDaysLeft : 7 ,
return v == null ? varsayilan : v ;
role : 'Yönetici Tedarikçi' , uyumEsigi : 60 , scrapedCapabilities : { scrapedAt : null , extractedSectors : [ ] ,
} catch {
extractedProducts : [ ] , extractedCertifications : [ ] , ihaleKonulari : [ ] , matchScoreBoost : 0 } } ) ;
return varsayilan ;
}
} ;
export const AppProvider = ( { children } ) => {
export const AppProvider = ( { children } ) => {
const [ activeTab , setActiveTab ] = useState ( 'list' ) ; // 'list', 'bulten', 'sonuc', 'takvim', 'sozlesme', 'kdv', 'fiyat', 'kanban', 'smart_filter_builder'
const [ activeTab , setActiveTab ] = useState ( 'list' ) ; // 'list', 'bulten', 'sonuc', 'takvim', 'sozlesme', 'kdv', 'fiyat', 'kanban', 'smart_filter_builder'
@@ -44,7 +41,7 @@ export const AppProvider = ({ children }) => {
try {
try {
const saved = localStorage . getItem ( TENDER _CACHE _KEY ) ;
const saved = localStorage . getItem ( TENDER _CACHE _KEY ) ;
const parsed = saved ? JSON . parse ( saved ) : null ;
const parsed = saved ? JSON . parse ( saved ) : null ;
return Array . isArray ( parsed ) ? parsed . map ( t => ( { ... t , takipte : false , gorev : null , notes : [ ] , checklist : [ ] , incelendi : false , atananPersonel : null , kanbanStatus : 'incelenecek' , bidDraft : null } ) ) : [ ] ;
return Array . isArray ( parsed ) ? parsed . map ( publicTender ) : [ ] ;
} catch {
} catch {
try { localStorage . removeItem ( TENDER _CACHE _KEY ) ; } catch { /* yok say */ }
try { localStorage . removeItem ( TENDER _CACHE _KEY ) ; } catch { /* yok say */ }
return [ ] ;
return [ ] ;
@@ -107,10 +104,7 @@ export const AppProvider = ({ children }) => {
const [ onlyValidOnly , setOnlyValidOnly ] = useState ( true ) ;
const [ onlyValidOnly , setOnlyValidOnly ] = useState ( true ) ;
const [ quickFilter , setQuickFilter ] = useState ( 'all' ) ;
const [ quickFilter , setQuickFilter ] = useState ( 'all' ) ;
const [ savedFilters , setSavedFilters ] = useState ( ( ) => {
const [ savedFilters , setSavedFilters ] = useState ( [ ] ) ;
const v = yerelOku ( 'ihaleist_saved_filters' , [ ] ) ;
return Array . isArray ( v ) ? v : [ ] ;
} ) ;
const [ isLiveMode , setIsLiveMode ] = useState ( true ) ;
const [ isLiveMode , setIsLiveMode ] = useState ( true ) ;
const [ isRefreshingLive , setIsRefreshingLive ] = useState ( false ) ;
const [ isRefreshingLive , setIsRefreshingLive ] = useState ( false ) ;
@@ -129,6 +123,12 @@ export const AppProvider = ({ children }) => {
. finally ( ( ) => setAuthReady ( true ) ) ;
. finally ( ( ) => setAuthReady ( true ) ) ;
} , [ ] ) ;
} , [ ] ) ;
useEffect ( ( ) => {
const sync = event => { if ( event . key === 'ihaleist_user' || event . key === 'ihaleist_token' ) setAuthUser ( getStoredUser ( ) ) ; } ;
window . addEventListener ( 'storage' , sync ) ;
return ( ) => window . removeEventListener ( 'storage' , sync ) ;
} , [ ] ) ;
// "Son girişten beri" kartı için önceki ziyaretin zamanı . Ziyaret = tarayı cı
// "Son girişten beri" kartı için önceki ziyaretin zamanı . Ziyaret = tarayı cı
// sekmesi oturumu (sessionStorage): sayfa yenilemek sayacı sı fı rlamaz, yeni
// sekmesi oturumu (sessionStorage): sayfa yenilemek sayacı sı fı rlamaz, yeni
// sekme/gün açmak önceki damgayı "önceki giriş" yapar. Eskiden sabit 14'tü.
// sekme/gün açmak önceki damgayı "önceki giriş" yapar. Eskiden sabit 14'tü.
@@ -136,42 +136,48 @@ export const AppProvider = ({ children }) => {
useEffect ( ( ) => {
useEffect ( ( ) => {
if ( ! authUser ) return ;
if ( ! authUser ) return ;
try {
try {
const anahtar = 'ihaleist_son_giris' ;
const anahtar = 'ihaleist_son_giris: ' + authUser . email ;
const onceki = Number ( localStorage . getItem ( anahtar ) ) || null ;
const onceki = Number ( localStorage . getItem ( anahtar ) ) || null ;
const oturum = sessionStorage . getItem ( 'ihaleist_oturum_giris' ) ;
const oturum = sessionStorage . getItem ( 'ihaleist_oturum_giris: ' + authUser . email ) ;
if ( oturum !== null ) { // aynı sekmede yenileme: damga sabit kalı r
if ( oturum !== null ) { // aynı sekmede yenileme: damga sabit kalı r
setOncekiGiris ( Number ( oturum ) || null ) ;
setOncekiGiris ( Number ( oturum ) || null ) ;
return ;
return ;
}
}
setOncekiGiris ( onceki ) ;
setOncekiGiris ( onceki ) ;
sessionStorage . setItem ( 'ihaleist_oturum_giris' , String ( onceki || 0 ) ) ;
sessionStorage . setItem ( 'ihaleist_oturum_giris: ' + authUser . email , String ( onceki || 0 ) ) ;
localStorage . setItem ( anahtar , String ( Date . now ( ) ) ) ;
localStorage . setItem ( anahtar , String ( Date . now ( ) ) ) ;
} catch { /* depolama kapalı ysa kart bugünü gösterir */ }
} catch { /* depolama kapalı ysa kart bugünü gösterir */ }
} , [ authUser ? . email ] ) ;
} , [ authUser ? . email ] ) ;
// Giriş yapı nca kullanı cı verisini SUNUCUDAN yükle (localStorage'ı ezer)
// Giriş yapı nca kullanı cı verisini SUNUCUDAN yükle (localStorage'ı ezer)
useEffect ( ( ) => {
useEffect ( ( ) => {
if ( ! authUser ) { setVeriSenkron ( false ) ; setPlan ( null ) ; return ; }
setVeriSenkron ( false ) ; setPlan ( null ) ; setSaveError ( null ) ;
setFavoriteKurumlar ( [ ] ) ; setTeamMembers ( [ ] ) ; setSavedFilters ( [ ] ) ; setCompetitorWatchlist ( [ ] ) ;
setReminderSettings ( defaultReminder ( ) ) ; setUserProfile ( defaultProfile ( authUser ) ) ;
setVaultDocuments ( [ ] ) ; setActiveTenderDetail ( null ) ; setAiAnalysisResults ( null ) ; setNotifications ( [ ] ) ; setOncekiGiris ( null ) ;
setTenders ( prev => prev . map ( publicTender ) ) ;
if ( ! authUser ) return ;
planYenile ( ) ;
planYenile ( ) ;
let cancelled = false ;
let cancelled = false ;
setVeriSenkron ( false ) ;
setVeriSenkron ( false ) ;
ilkYazmaAtlanan . current . clear ( ) ;
ilkYazmaAtlanan . current . clear ( ) ;
savedTenderStates . current = { } ;
savedTenderStates . current = { } ;
const loadSession = getToken ( ) ;
Promise . all ( [ getAllUserData ( ) , getTenderStates ( ) ] )
Promise . all ( [ getAllUserData ( ) , getTenderStates ( ) ] )
. then ( ( [ v , records ] ) => {
. then ( ( [ v , records ] ) => {
if ( cancelled ) return ;
if ( cancelled || getToken ( ) !== loadSession ) return ;
setFavoriteKurumlar ( v . favori _kurumlar || [ ] ) ;
setFavoriteKurumlar ( v . favori _kurumlar || [ ] ) ;
setTeamMembers ( v . ekip || [ ] ) ;
setTeamMembers ( v . ekip || [ ] ) ;
setSavedFilters ( v . saved _filters || [ ] ) ;
setSavedFilters ( v . saved _filters || [ ] ) ;
if ( v . reminder ) setReminderSettings ( ( s ) => ( { ... s , ... v . reminder } ) ) ;
setReminderSettings ( { ... defaultReminder ( ) , ... v . reminder } ) ;
setCompetitorWatchlist ( v . rakip _izleme || [ ] ) ;
setCompetitorWatchlist ( v . rakip _izleme || [ ] ) ;
if ( v . firma _profil ) setUserProfile ( p => ( { ... p , ... v . firma _profil } ) ) ;
setUserProfile ( { ... defaultProfile ( authUser ) , ... v . firma _profil } ) ;
savedTenderStates . current = records ;
savedTenderStates . current = records ;
setTenders ( prev => applyTenderStates ( prev , records ) ) ;
setTenders ( prev => applyTenderStates ( prev . map ( publicTender ) , records ) ) ;
setVeriSenkron ( true ) ;
setVeriSenkron ( true ) ;
setSaveError ( null ) ;
setSaveError ( null ) ;
} )
} )
. catch ( ( e ) => { if ( ! cancelled ) setSaveError ( 'Hesap verileri yüklenemedi: ' + e . message ) ; } ) ;
. catch ( ( e ) => { if ( ! cancelled && getToken ( ) === loadSession ) setSaveError ( 'Hesap verileri yüklenemedi: ' + e . message ) ; } ) ;
return ( ) => { cancelled = true ; } ;
return ( ) => { cancelled = true ; } ;
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
} , [ authUser ? . email ] ) ;
} , [ authUser ? . email ] ) ;
@@ -190,21 +196,23 @@ export const AppProvider = ({ children }) => {
const key = tenderStateKey ( t ) ;
const key = tenderStateKey ( t ) ;
if ( inFlight . current . has ( key ) ) return false ;
if ( inFlight . current . has ( key ) ) return false ;
const owner = authUser . email ;
const owner = authUser . email ;
const session = getToken ( ) ;
inFlight . current . add ( key ) ; setSavingCount ( n => n + 1 ) ; setSaveError ( null ) ;
inFlight . current . add ( key ) ; setSavingCount ( n => n + 1 ) ; setSaveError ( null ) ;
try {
try {
const record = await saveTenderState ( key , patch , tenderSnapshot ( t ) , savedTenderStates . current [ key ] ? . version || 0 ) ;
const record = await saveTenderState ( key , patch , tenderSnapshot ( t ) , savedTenderStates . current [ key ] ? . version || 0 ) ;
if ( getStoredUser ( ) ? . email !== owner ) return false ;
if ( getStoredUser ( ) ? . email !== owner || getToken ( ) !== session ) return false ;
savedTenderStates . current = { ... savedTenderStates . current , [ key ] : record } ;
savedTenderStates . current = { ... savedTenderStates . current , [ key ] : record } ;
setTenders ( prev => applyTenderStates ( prev , savedTenderStates . current ) ) ;
setTenders ( prev => applyTenderStates ( prev , savedTenderStates . current ) ) ;
return true ;
return true ;
} catch ( e ) {
} catch ( e ) {
if ( getStoredUser ( ) ? . email !== owner || getToken ( ) !== session ) return false ;
setSaveError ( e . message ) ;
setSaveError ( e . message ) ;
if ( e . kod === 'VERSION_CONFLICT' ) {
if ( e . kod === 'VERSION_CONFLICT' ) {
try {
try {
const records = await getTenderStates ( ) ;
const records = await getTenderStates ( ) ;
if ( getStoredUser ( ) ? . email === owner ) {
if ( getStoredUser ( ) ? . email === owner && getToken ( ) === session ) {
savedTenderStates . current = records ;
savedTenderStates . current = records ;
setTenders ( prev => applyTenderStates ( prev , records ) ) ;
setTenders ( prev => applyTenderStates ( prev . map ( publicTender ) , records ) ) ;
}
}
} catch { /* retain visible error */ }
} catch { /* retain visible error */ }
}
}
@@ -223,15 +231,17 @@ export const AppProvider = ({ children }) => {
const sunucuyaYaz = ( anahtar , deger ) => {
const sunucuyaYaz = ( anahtar , deger ) => {
if ( ! authUser ) return ;
if ( ! authUser || ! veriSenkron || getStoredUser ( ) ? . email !== authUser . email ) return ;
const session = getToken ( ) ;
setUserData ( anahtar , deger ) . catch ( ( e ) => {
setUserData ( anahtar , deger ) . catch ( ( e ) => {
if ( getToken ( ) !== session ) return ;
if ( e . kod === 'KOTA_ASILDI' ) setKotaUyarisi ( e . message ) ;
if ( e . kod === 'KOTA_ASILDI' ) setKotaUyarisi ( e . message ) ;
else setSaveError ( 'Kaydedilemedi: ' + e . message ) ;
else setSaveError ( 'Kaydedilemedi: ' + e . message ) ;
} ) ;
} ) ;
} ;
} ;
const [ kotaUyarisi , setKotaUyarisi ] = useState ( null ) ;
const [ kotaUyarisi , setKotaUyarisi ] = useState ( null ) ;
const planYenile = ( ) => getPlan ( ) . then ( setPlan ) . catch ( ( ) => { } ) ;
const planYenile = ( ) => { const session = getToken ( ) ; return getPlan ( ) . then ( p => { if ( getToken ( ) === session ) setPlan ( p ) ; } ) . catch ( ( ) => { } ) ; } ;
const handleLogin = ( user ) => setAuthUser ( user ) ;
const handleLogin = ( user ) => setAuthUser ( user ) ;
const handleLogout = ( ) => {
const handleLogout = ( ) => {
@@ -303,21 +313,15 @@ export const AppProvider = ({ children }) => {
const [ aiAnalyzing , setAiAnalyzing ] = useState ( false ) ;
const [ aiAnalyzing , setAiAnalyzing ] = useState ( false ) ;
// Enterprise Module States
// Enterprise Module States
// Rakip izleme listesi — kullanı cı gerçek kazanan firma analizinden ekler (localStorage )
// Rakip izleme listesi — kullanı cı gerçek kazanan firma analizinden ekler (hesaba özel sunucu kaydı )
const [ competitorWatchlist , setCompetitorWatchlist ] = useState ( ( ) => {
const [ competitorWatchlist , setCompetitorWatchlist ] = useState ( [ ] ) ;
try { return JSON . parse ( localStorage . getItem ( 'ihaleist_rakip_izleme' ) || '[]' ) ; } catch { return [ ] ; }
} ) ;
useEffect ( ( ) => {
useEffect ( ( ) => {
yerelKaydet ( 'ihaleist_rakip_izleme' , competitorWatchlist ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'rakip_izleme' , competitorWatchlist ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'rakip_izleme' , competitorWatchlist ) ;
} , [ competitorWatchlist , veriSenkron ] ) ;
} , [ competitorWatchlist , veriSenkron ] ) ;
// --- Favori kurumlar (localStorage, gerçek ) ---
// --- Favori kurumlar (sunucu kaydı ) ---
const [ favoriteKurumlar , setFavoriteKurumlar ] = useState ( ( ) => {
const [ favoriteKurumlar , setFavoriteKurumlar ] = useState ( [ ] ) ;
try { return JSON . parse ( localStorage . getItem ( 'ihaleist_favori_kurumlar' ) || '[]' ) ; } catch { return [ ] ; }
} ) ;
useEffect ( ( ) => {
useEffect ( ( ) => {
yerelKaydet ( 'ihaleist_favori_kurumlar' , favoriteKurumlar ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'favori_kurumlar' , favoriteKurumlar ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'favori_kurumlar' , favoriteKurumlar ) ;
} , [ favoriteKurumlar , veriSenkron ] ) ;
} , [ favoriteKurumlar , veriSenkron ] ) ;
const toggleFavoriteKurum = ( kurum ) => {
const toggleFavoriteKurum = ( kurum ) => {
@@ -326,44 +330,19 @@ export const AppProvider = ({ children }) => {
setFavoriteKurumlar ( ( prev ) => ( prev . includes ( ad ) ? prev . filter ( ( k ) => k !== ad ) : [ ... prev , ad ] ) ) ;
setFavoriteKurumlar ( ( prev ) => ( prev . includes ( ad ) ? prev . filter ( ( k ) => k !== ad ) : [ ... prev , ad ] ) ) ;
} ;
} ;
// --- Ekip yönetimi (localStorage, gerçek CRUD ) ---
// --- Ekip yönetimi (sunucu kaydı ) ---
const [ teamMembers , setTeamMembers ] = useState ( ( ) => {
const [ teamMembers , setTeamMembers ] = useState ( [ ] ) ;
try {
const saved = localStorage . getItem ( 'ihaleist_ekip' ) ;
if ( saved ) return JSON . parse ( saved ) ;
} catch { /* yoksay */ }
const u = getStoredUser ( ) ;
return u ? [ { id : 'tm-1' , name : u . name || u . email , email : u . email || '' , role : 'Yönetici' } ] : [ ] ;
} ) ;
useEffect ( ( ) => {
useEffect ( ( ) => {
yerelKaydet ( 'ihaleist_ekip' , teamMembers ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'ekip' , teamMembers ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'ekip' , teamMembers ) ;
} , [ teamMembers , veriSenkron ] ) ;
} , [ teamMembers , veriSenkron ] ) ;
const addTeamMember = ( m ) => setTeamMembers ( ( prev ) => [ ... prev , { id : 'tm-' + Date . now ( ) , ... m } ] ) ;
const addTeamMember = ( m ) => setTeamMembers ( ( prev ) => [ ... prev , { id : 'tm-' + Date . now ( ) , ... m } ] ) ;
const removeTeamMember = ( id ) => setTeamMembers ( ( prev ) => prev . filter ( ( m ) => m . id !== id ) ) ;
const removeTeamMember = ( id ) => setTeamMembers ( ( prev ) => prev . filter ( ( m ) => m . id !== id ) ) ;
// Belge kasası — kullanı cı kendi belgelerini ekler (localStorage )
// Belge kasası — kullanı cı kendi belgelerini ekler (hesaba özel sunucu kaydı )
const [ vaultDocuments , setVaultDocuments ] = useState ( ( ) => {
const [ vaultDocuments , setVaultDocuments ] = useState ( [ ] ) ;
try { return JSON . parse ( localStorage . getItem ( 'ihaleist_belgeler' ) || '[]' ) ; } catch { return [ ] ; }
} ) ;
useEffect ( ( ) => {
yerelKaydet ( 'ihaleist_belgeler' , vaultDocuments ) ;
} , [ vaultDocuments ] ) ;
const [ reminderSettings , setReminderSettings ] = useState ( ( ) => {
const [ reminderSettings , setReminderSettings ] = useState ( defaultReminder ) ;
const def = {
emailEnabled : true , smsEnabled : true , pushEnabled : true ,
notify3DaysBefore : true , notify1DayBefore : true , notify3HoursBefore : true ,
googleCalendarSync : true , outlookSync : false ,
quietEnabled : false , quietStart : '22:00' , quietEnd : '08:00' ,
} ;
try {
const saved = JSON . parse ( localStorage . getItem ( 'ihaleist_reminder' ) || 'null' ) ;
return saved ? { ... def , ... saved } : def ;
} catch { return def ; }
} ) ;
useEffect ( ( ) => {
useEffect ( ( ) => {
yerelKaydet ( 'ihaleist_reminder' , reminderSettings ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'reminder' , reminderSettings ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'reminder' , reminderSettings ) ;
} , [ reminderSettings , veriSenkron ] ) ;
} , [ reminderSettings , veriSenkron ] ) ;
@@ -441,6 +420,7 @@ export const AppProvider = ({ children }) => {
// seçilen model zinciri (fallback) ile.
// seçilen model zinciri (fallback) ile.
// mode='derin' → ince detaylar, belge kontrol listesi ve yol haritası da üretilir
// mode='derin' → ince detaylar, belge kontrol listesi ve yol haritası da üretilir
const analyzeSpecPdfWithGemini = async ( { text , fileName , tender , mode } = { } ) => {
const analyzeSpecPdfWithGemini = async ( { text , fileName , tender , mode } = { } ) => {
const session = getToken ( ) ;
setAiAnalyzing ( true ) ;
setAiAnalyzing ( true ) ;
setAiAnalysisResults ( null ) ;
setAiAnalysisResults ( null ) ;
try {
try {
@@ -457,6 +437,7 @@ export const AppProvider = ({ children }) => {
attempts : data . attempts ,
attempts : data . attempts ,
analyzedAt : new Date ( data . analyzedAt ) . toLocaleString ( 'tr-TR' ) ,
analyzedAt : new Date ( data . analyzedAt ) . toLocaleString ( 'tr-TR' ) ,
} ;
} ;
if ( getToken ( ) !== session ) throw new Error ( 'Oturum değişti; analiz sonucu bu hesaba aktarı lmadı .' ) ;
setAiAnalysisResults ( result ) ;
setAiAnalysisResults ( result ) ;
return result ;
return result ;
} finally {
} finally {
@@ -464,33 +445,12 @@ export const AppProvider = ({ children }) => {
}
}
} ;
} ;
const BOS _YETKINLIK = {
const [ userProfile , setUserProfile ] = useState ( ( ) => defaultProfile ( getStoredUser ( ) ) ) ;
scrapedAt : null , extractedSectors : [ ] , extractedProducts : [ ] , extractedCertifications : [ ] ,
ihaleKonulari : [ ] , matchScoreBoost : 0 ,
} ;
const [ userProfile , setUserProfile ] = useState ( ( ) => {
// 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 : 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 || { } ) } ,
// Firma eşiği: "uyumlu" filtresi ve bildirim bu skordan itibaren
uyumEsigi : Number . isFinite ( kayitli ? . uyumEsigi ) ? kayitli . uyumEsigi : 60 ,
} ;
} ) ;
useEffect ( ( ) => {
useEffect ( ( ) => {
const veri = {
const veri = {
company : userProfile . company , website : userProfile . website ,
company : userProfile . company , website : userProfile . website ,
scrapedCapabilities : userProfile . scrapedCapabilities , uyumEsigi : userProfile . uyumEsigi ,
scrapedCapabilities : userProfile . scrapedCapabilities , uyumEsigi : userProfile . uyumEsigi ,
} ;
} ;
yerelKaydet ( 'ihaleist_firma_profil' , veri ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'firma_profil' , veri ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'firma_profil' , veri ) ;
} , [ userProfile . company , userProfile . website , userProfile . scrapedCapabilities , userProfile . uyumEsigi , veriSenkron ] ) ;
} , [ 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 ) ) } ) ) ;
const setUyumEsigi = ( v ) => setUserProfile ( ( p ) => ( { ... p , uyumEsigi : Math . max ( 0 , Math . min ( 100 , Number ( v ) || 0 ) ) } ) ) ;
@@ -532,6 +492,7 @@ export const AppProvider = ({ children }) => {
// Önce gerçek tarama (/api/company/scan: site metni + model), başarı sı zsa
// Önce gerçek tarama (/api/company/scan: site metni + model), başarı sı zsa
// alan adı ndan tahmin eden eski yol. Sonuç uyum skorunun girdisi olur.
// alan adı ndan tahmin eden eski yol. Sonuç uyum skorunun girdisi olur.
const scanCompanyWebsite = async ( url ) => {
const scanCompanyWebsite = async ( url ) => {
const session = getToken ( ) ;
let yeni = null ;
let yeni = null ;
try {
try {
const r = await fetch ( '/api/company/scan' , {
const r = await fetch ( '/api/company/scan' , {
@@ -556,6 +517,7 @@ export const AppProvider = ({ children }) => {
}
}
} catch { /* aşağıda yedek yol */ }
} catch { /* aşağıda yedek yol */ }
if ( ! yeni ) yeni = { ... parseWebsiteCapabilitiesDynamically ( url ) , ihaleKonulari : [ ] , gercekTarama : false } ;
if ( ! yeni ) yeni = { ... parseWebsiteCapabilitiesDynamically ( url ) , ihaleKonulari : [ ] , gercekTarama : false } ;
if ( getToken ( ) !== session ) throw new Error ( 'Oturum değişti; firma taraması bu hesaba aktarı lmadı .' ) ;
setUserProfile ( ( prev ) => ( { ... prev , website : url , scrapedCapabilities : yeni } ) ) ;
setUserProfile ( ( prev ) => ( { ... prev , website : url , scrapedCapabilities : yeni } ) ) ;
return yeni ;
return yeni ;
} ;
} ;
@@ -596,7 +558,7 @@ export const AppProvider = ({ children }) => {
// boyamaya yetecek kadarı nı , hataya dayanı klı biçimde saklı yoruz.
// boyamaya yetecek kadarı nı , hataya dayanı klı biçimde saklı yoruz.
useEffect ( ( ) => {
useEffect ( ( ) => {
try {
try {
localStorage . setItem ( TENDER _CACHE _KEY , JSON . stringify ( tenders . slice ( 0 , TENDER _CACHE _LIMIT ) ) ) ;
localStorage . setItem ( TENDER _CACHE _KEY , JSON . stringify ( tenders . slice ( 0 , TENDER _CACHE _LIMIT ) . map ( publicTender ) ) ) ;
} catch {
} catch {
// Kota dolduysa önbelleği bı rak — veri zaten sunucudan geliyor.
// Kota dolduysa önbelleği bı rak — veri zaten sunucudan geliyor.
try { localStorage . removeItem ( TENDER _CACHE _KEY ) ; } catch { /* yok say */ }
try { localStorage . removeItem ( TENDER _CACHE _KEY ) ; } catch { /* yok say */ }
@@ -604,7 +566,6 @@ export const AppProvider = ({ children }) => {
} , [ tenders ] ) ;
} , [ tenders ] ) ;
useEffect ( ( ) => {
useEffect ( ( ) => {
yerelKaydet ( 'ihaleist_saved_filters' , savedFilters ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'saved_filters' , savedFilters ) ;
if ( veriSenkron ) ilkYazmaDegil ( 'saved_filters' , savedFilters ) ;
} , [ savedFilters , veriSenkron ] ) ;
} , [ savedFilters , veriSenkron ] ) ;
@@ -654,11 +615,12 @@ export const AppProvider = ({ children }) => {
useEffect ( ( ) => {
useEffect ( ( ) => {
if ( ! veriSenkron ) return ;
if ( ! veriSenkron ) return ;
let iptal = false ;
let iptal = false ;
const session = getToken ( ) ;
const cek = async ( ) => {
const cek = async ( ) => {
try {
try {
const liste = await bildirimleriGetir ( ) ;
const liste = await bildirimleriGetir ( ) ;
const yeni = liste . filter ( ( b ) => ! b . okundu ) ;
const yeni = liste . filter ( ( b ) => ! b . okundu ) ;
if ( iptal || ! yeni . length ) return ;
if ( iptal || getToken ( ) !== session || ! yeni . length ) return ;
for ( const b of yeni . slice ( 0 , 5 ) ) notify ( b . baslik , b . metin ) ;
for ( const b of yeni . slice ( 0 , 5 ) ) notify ( b . baslik , b . metin ) ;
await bildirimOkundu ( yeni . map ( ( b ) => b . id ) ) ;
await bildirimOkundu ( yeni . map ( ( b ) => b . id ) ) ;
} catch { /* sessiz */ }
} catch { /* sessiz */ }
@@ -674,9 +636,12 @@ export const AppProvider = ({ children }) => {
// fark etmiyor — fikir: MOBIT-DURT).
// fark etmiyor — fikir: MOBIT-DURT).
useEffect ( ( ) => {
useEffect ( ( ) => {
if ( ! veriSenkron ) return ;
if ( ! veriSenkron ) return ;
const session = getToken ( ) ;
const uyariAnahtari = 'ihaleist_belge_uyari_gunu:' + authUser ? . email ;
const bugun = new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) ;
const bugun = new Date ( ) . toISOString ( ) . slice ( 0 , 10 ) ;
try { if ( localStorage . getItem ( 'ihaleist_belge_uyari_gunu' ) === bugun ) return ; } catch { /* yoksay */ }
try { if ( localStorage . getItem ( uyariAnahtari ) === bugun ) return ; } catch { /* yoksay */ }
listDocuments ( ) . then ( ( d ) => {
listDocuments ( ) . then ( ( d ) => {
if ( getToken ( ) !== session ) return ;
const simdi = Date . now ( ) ;
const simdi = Date . now ( ) ;
const dolan = [ ] , yakin = [ ] ;
const dolan = [ ] , yakin = [ ] ;
for ( const b of d ? . belgeler || [ ] ) {
for ( const b of d ? . belgeler || [ ] ) {
@@ -686,7 +651,7 @@ export const AppProvider = ({ children }) => {
}
}
if ( dolan . length ) notify ( ` ${ dolan . length } belgenin süresi dolmuş ` , dolan . slice ( 0 , 4 ) . join ( ', ' ) ) ;
if ( dolan . length ) notify ( ` ${ dolan . length } belgenin süresi dolmuş ` , dolan . slice ( 0 , 4 ) . join ( ', ' ) ) ;
if ( yakin . length ) notify ( ` ${ yakin . length } belgenin süresi 30 gün içinde doluyor ` , yakin . slice ( 0 , 4 ) . join ( ', ' ) ) ;
if ( yakin . length ) notify ( ` ${ yakin . length } belgenin süresi 30 gün içinde doluyor ` , yakin . slice ( 0 , 4 ) . join ( ', ' ) ) ;
try { localStorage . setItem ( 'ihaleist_belge_uyari_gunu' , bugun ) ; } catch { /* yoksay */ }
try { localStorage . setItem ( uyariAnahtari , bugun ) ; } catch { /* yoksay */ }
} ) . catch ( ( ) => { } ) ;
} ) . catch ( ( ) => { } ) ;
// eslint-disable-next-line react-hooks/exhaustive-deps
// eslint-disable-next-line react-hooks/exhaustive-deps
} , [ veriSenkron ] ) ;
} , [ veriSenkron ] ) ;