feat: tüm mock veriyi kaldır + Resend e-posta entegrasyonu
- mockTenders.js silindi; referans listeleri (sektör/il/kurum) constants.js'e taşındı - AppContext: tenders/liveStats/competitorWatchlist/vaultDocuments/notifications/ savedFilters/apiKeys/scrapedCapabilities → gerçek/boş; sahte Math.random online sayacı kaldırıldı; competitor & belge kasası artık localStorage - LiveStatsBar: gerçek alanlar (canlı havuz/yüklü/bugün/kalem/takibimdeki) - AnalyticsDashboard: KPI'lar (katılım/kazanma oranı/kazanılan bedel) + aylık hacim + sektörel dağılım gerçek companyStats/monthlyStats/sectorStats'tan; ciro hedefi kullanıcı tarafından belirlenir; veri yoksa dürüst boş durum - ContractSearch & Rakip Radar: gerçek firmStats - analytics.js: companyStats/monthlyStats/parseDateTR eklendi - Resend: server/email.mjs + /api/notify/email/config + /api/notify/email/test (auth'lu, kullanıcının kendi adresine); PwaSettings gerçek e-posta test kartı
This commit is contained in:
+32
-61
@@ -1,5 +1,4 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import { MOCK_TENDERS, MOCK_LIVE_STATS, MOCK_COMPANY_STATS } from '../data/mockTenders';
|
||||
import { parseWebsiteCapabilitiesDynamically } from '../services/webScraperService';
|
||||
import { fetchLiveTenders, fetchTenderDetail, mergeUserState } from '../services/tenderApi';
|
||||
import { restoreSession, clearSession, getStoredUser } from '../services/auth';
|
||||
@@ -10,10 +9,10 @@ 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) : MOCK_TENDERS;
|
||||
return saved ? JSON.parse(saved) : [];
|
||||
});
|
||||
|
||||
const [liveStats, setLiveStats] = useState(MOCK_LIVE_STATS);
|
||||
const [liveStats, setLiveStats] = useState({ toplamIhale: 0, bugunEklenen: 0 });
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedCity, setSelectedCity] = useState('');
|
||||
const [selectedAlimTuru, setSelectedAlimTuru] = useState('');
|
||||
@@ -24,10 +23,7 @@ export const AppProvider = ({ children }) => {
|
||||
|
||||
const [savedFilters, setSavedFilters] = useState(() => {
|
||||
const saved = localStorage.getItem('ihaleist_saved_filters');
|
||||
return saved ? JSON.parse(saved) : [
|
||||
{ id: 'sf-1', name: 'Sağlık Hizmeti İhaleleri', user: 'hikmet', kurumTipi: 'Eğitim ve Araştırma Hastaneleri', sehir: 'Ankara' },
|
||||
{ id: 'sf-2', name: 'Kayseri Kırtasiye Alımları', user: 'hikmet', kurumTipi: 'Ceza İnfaz Kurumları', sehir: 'Kayseri' }
|
||||
];
|
||||
return saved ? JSON.parse(saved) : [];
|
||||
});
|
||||
|
||||
const [isLiveMode, setIsLiveMode] = useState(true);
|
||||
@@ -61,11 +57,10 @@ export const AppProvider = ({ children }) => {
|
||||
const data = await fetchLiveTenders({ refresh });
|
||||
setTenders(prev => mergeUserState(data.tenders, prev));
|
||||
setLiveDataSource({ source: data.source, fetchedAt: data.fetchedAt });
|
||||
setLiveStats(prev => ({
|
||||
...prev,
|
||||
bugunEklenen: data.todayCount ?? prev.bugunEklenen,
|
||||
aktifTakip: data.totalCount ?? prev.aktifTakip
|
||||
}));
|
||||
setLiveStats({
|
||||
toplamIhale: data.totalCount ?? 0,
|
||||
bugunEklenen: data.todayCount ?? 0,
|
||||
});
|
||||
} catch (err) {
|
||||
setLiveDataError(err.message);
|
||||
} finally {
|
||||
@@ -107,11 +102,13 @@ export const AppProvider = ({ children }) => {
|
||||
const [aiAnalysisResults, setAiAnalysisResults] = useState(null);
|
||||
|
||||
// Enterprise Module States
|
||||
const [competitorWatchlist, setCompetitorWatchlist] = useState([
|
||||
{ id: 'cw-1', name: 'Bileşim Tıbbi Cihazlar A.Ş.', sector: 'Medikal & Tıbbi Malzeme', winCount: 24, totalRevenue: 14200000, marketShare: 32, status: 'Aktif İzleniyor', alertOnBid: true, alertOnWin: true },
|
||||
{ id: 'cw-2', name: 'Atlas Sağlık ve Kırtasiye Ltd.', sector: 'Sağlık & Büro Malzemeleri', winCount: 18, totalRevenue: 9800000, marketShare: 24, status: 'Aktif İzleniyor', alertOnBid: true, alertOnWin: true },
|
||||
{ id: 'cw-3', name: 'Tekno-Med Laboratuvar Sistemleri', sector: 'Laboratuvar & Bilişim', winCount: 12, totalRevenue: 6500000, marketShare: 16, status: 'Aktif İzleniyor', alertOnBid: false, alertOnWin: true }
|
||||
]);
|
||||
// Rakip izleme listesi — kullanıcı gerçek kazanan firma analizinden ekler (localStorage)
|
||||
const [competitorWatchlist, setCompetitorWatchlist] = useState(() => {
|
||||
try { return JSON.parse(localStorage.getItem('ihaleist_rakip_izleme') || '[]'); } catch { return []; }
|
||||
});
|
||||
useEffect(() => {
|
||||
localStorage.setItem('ihaleist_rakip_izleme', JSON.stringify(competitorWatchlist));
|
||||
}, [competitorWatchlist]);
|
||||
|
||||
// --- Favori kurumlar (localStorage, gerçek) ---
|
||||
const [favoriteKurumlar, setFavoriteKurumlar] = useState(() => {
|
||||
@@ -142,13 +139,13 @@ export const AppProvider = ({ children }) => {
|
||||
const addTeamMember = (m) => setTeamMembers((prev) => [...prev, { id: 'tm-' + Date.now(), ...m }]);
|
||||
const removeTeamMember = (id) => setTeamMembers((prev) => prev.filter((m) => m.id !== id));
|
||||
|
||||
const [vaultDocuments, setVaultDocuments] = useState([
|
||||
{ id: 'doc-1', name: 'İmza Sirküleri 2026.pdf', category: 'Yasal Belgeler', size: '2.4 MB', updatedAt: '2026-01-15', expiresAt: '2027-01-15', status: 'Geçerli' },
|
||||
{ id: 'doc-2', name: 'ISO 13485 Tıbbi Cihaz Sertifikası.pdf', category: 'Kalite Belgeleri', size: '1.8 MB', updatedAt: '2025-11-10', expiresAt: '2028-11-10', status: 'Geçerli' },
|
||||
{ id: 'doc-3', name: 'Oda Faaliyet Belgesi.pdf', category: 'Resmi Kayıtlar', size: '1.1 MB', updatedAt: '2026-02-01', expiresAt: '2026-08-01', status: 'Geçerli' },
|
||||
{ id: 'doc-4', name: 'ÜTS Firma Kayıt Belgesi.pdf', category: 'Tıbbi Cihaz Kaydı', size: '3.2 MB', updatedAt: '2026-03-20', expiresAt: '2027-03-20', status: 'Geçerli' },
|
||||
{ id: 'doc-5', name: 'Vergi Levhası 2025.pdf', category: 'Mali Belgeler', size: '850 KB', updatedAt: '2025-05-30', expiresAt: '2026-05-30', status: 'Geçerli' }
|
||||
]);
|
||||
// Belge kasası — kullanıcı kendi belgelerini ekler (localStorage)
|
||||
const [vaultDocuments, setVaultDocuments] = useState(() => {
|
||||
try { return JSON.parse(localStorage.getItem('ihaleist_belgeler') || '[]'); } catch { return []; }
|
||||
});
|
||||
useEffect(() => {
|
||||
localStorage.setItem('ihaleist_belgeler', JSON.stringify(vaultDocuments));
|
||||
}, [vaultDocuments]);
|
||||
|
||||
const [reminderSettings, setReminderSettings] = useState(() => {
|
||||
const def = {
|
||||
@@ -178,8 +175,8 @@ export const AppProvider = ({ children }) => {
|
||||
};
|
||||
|
||||
const [webhooksConfig, setWebhooksConfig] = useState({
|
||||
slackWebhookUrl: 'https://hooks.slack.com/services/T00/B00/XXXXXX',
|
||||
teamsWebhookUrl: 'https://outlook.office.com/webhook/XXXXXX',
|
||||
slackWebhookUrl: '',
|
||||
teamsWebhookUrl: '',
|
||||
events: {
|
||||
newTenderMatch: true,
|
||||
tenderWon: true,
|
||||
@@ -188,9 +185,7 @@ export const AppProvider = ({ children }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const [apiKeys, setApiKeys] = useState([
|
||||
{ id: 'key-1', name: 'ERP Entegrasyon Anahtarı', key: 'ih_live_8f92a19c3b88412e', createdAt: '2026-04-10', lastUsed: 'Bugün 12:45' }
|
||||
]);
|
||||
const [apiKeys, setApiKeys] = useState([]);
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// AI Model Yönetimi (admin panelden seçilebilir + fallback zinciri)
|
||||
@@ -273,23 +268,11 @@ export const AppProvider = ({ children }) => {
|
||||
trialDaysLeft: 7,
|
||||
role: 'Yönetici Tedarikçi',
|
||||
scrapedCapabilities: {
|
||||
scrapedAt: '2026-08-07 14:30',
|
||||
extractedSectors: ['Tıbbi Cihaz ve Sarf Malzemeleri', 'Sağlık & Laboratuvar Teknolojileri'],
|
||||
extractedProducts: [
|
||||
'Ortopedik Cerrahi İmplantlar ve Protezler',
|
||||
'Cerrahi Maske ve Steril Önlük Setleri',
|
||||
'Tıbbi Ameliyathane & Yoğun Bakım Karyolaları',
|
||||
'Dezenfektan ve Hastane Sterilizasyon Solüsyonları',
|
||||
'Tek Kullanımlık Enjektör ve İğne Uçları',
|
||||
'Laboratuvar Reaktif ve Biyokimya Test Kitleri'
|
||||
],
|
||||
extractedCertifications: [
|
||||
'ISO 13485 Tıbbi Cihaz Kalite Yönetim Sertifikası',
|
||||
'ÜTS (Ürün Takip Sistemi) Firma Kaydı & Barkod Yetkisi',
|
||||
'CE Tıbbi Cihaz Uygunluk Beyanı',
|
||||
'TSE HYB Belgesi (Medikal Cihaz Bakım-Onarım)'
|
||||
],
|
||||
matchScoreBoost: 20
|
||||
scrapedAt: null,
|
||||
extractedSectors: [],
|
||||
extractedProducts: [],
|
||||
extractedCertifications: [],
|
||||
matchScoreBoost: 0
|
||||
}
|
||||
});
|
||||
|
||||
@@ -319,10 +302,7 @@ export const AppProvider = ({ children }) => {
|
||||
}));
|
||||
};
|
||||
|
||||
const [notifications, setNotifications] = useState([
|
||||
{ id: 'n1', title: 'Yeni İhale Bildirimi', text: 'Sağlık Hizmeti İhaleleri filtrenize 3 yeni ihale eklendi.', time: '5dk önce', read: false },
|
||||
{ id: 'n2', title: 'Yaklaşan İhale Süresi', text: 'KIRTASİYE MALZEMELERİ ALIMI ihaleniz için son 3 gün!', time: '1sa önce', read: false }
|
||||
]);
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
|
||||
// Gerçek bildirim: listeye ekler; sessiz saat dışında ve push açıksa tarayıcı
|
||||
// bildirimi (Notification API) da gösterir.
|
||||
@@ -351,17 +331,8 @@ export const AppProvider = ({ children }) => {
|
||||
localStorage.setItem('ihaleist_saved_filters', JSON.stringify(savedFilters));
|
||||
}, [savedFilters]);
|
||||
|
||||
// Live counter animation effect
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
setLiveStats(prev => ({
|
||||
...prev,
|
||||
onlineKullanici: prev.onlineKullanici + (Math.floor(Math.random() * 5) - 2),
|
||||
bugunEklenen: prev.bugunEklenen + (Math.random() > 0.7 ? 1 : 0)
|
||||
}));
|
||||
}, 4000);
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
// (Sahte "online kullanıcı" rastgele sayacı kaldırıldı — canlı istatistikler
|
||||
// gerçek /api/stats verisinden gelir.)
|
||||
|
||||
const toggleTrackTender = (tenderId) => {
|
||||
setTenders(prev => prev.map(t => {
|
||||
|
||||
Reference in New Issue
Block a user