feat: İhaleist - Akıllı İhale Takip ve Analiz Platformu (Full PRD + 7 Enterprise Modül)

This commit is contained in:
2026-08-07 16:30:24 +03:00
commit ece74feefe
35 changed files with 7820 additions and 0 deletions
+297
View File
@@ -0,0 +1,297 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
import { MOCK_TENDERS, MOCK_LIVE_STATS, MOCK_COMPANY_STATS } from '../data/mockTenders';
const AppContext = createContext();
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;
});
const [liveStats, setLiveStats] = useState(MOCK_LIVE_STATS);
const [searchQuery, setSearchQuery] = useState('');
const [selectedCity, setSelectedCity] = useState('');
const [selectedAlimTuru, setSelectedAlimTuru] = useState('');
const [selectedKurumTipi, setSelectedKurumTipi] = useState('');
const [selectedSektor, setSelectedSektor] = useState('');
const [onlyValidOnly, setOnlyValidOnly] = useState(true);
const [quickFilter, setQuickFilter] = useState('all');
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' }
];
});
const [activeTenderDetail, setActiveTenderDetail] = useState(null);
const [isDetailedSearchOpen, setIsDetailedSearchOpen] = useState(false);
const [isCompanyScannerOpen, setIsCompanyScannerOpen] = useState(false);
const [isAiSpecAnalyzerOpen, setIsAiSpecAnalyzerOpen] = useState(false);
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 }
]);
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' }
]);
const [reminderSettings, setReminderSettings] = useState({
emailEnabled: true,
smsEnabled: true,
pushEnabled: true,
notify3DaysBefore: true,
notify1DayBefore: true,
notify3HoursBefore: true,
googleCalendarSync: true,
outlookSync: false
});
const [webhooksConfig, setWebhooksConfig] = useState({
slackWebhookUrl: 'https://hooks.slack.com/services/T00/B00/XXXXXX',
teamsWebhookUrl: 'https://outlook.office.com/webhook/XXXXXX',
events: {
newTenderMatch: true,
tenderWon: true,
bidDeadlineAlert: true,
competitorActivity: true
}
});
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 analyzeSpecPdfWithGemini = (fileName = 'Teknik_Sartname.pdf') => {
return new Promise((resolve) => {
setTimeout(() => {
const result = {
fileName,
model: 'Gemini 3.6 Flash',
analyzedAt: new Date().toLocaleString('tr-TR'),
summary: 'İşbu teknik şartname, idarenin ihtiyacı olan medikal sarf malzemeleri ve cihaz parçalarının teknik özellikleri, garanti şartları, teslimat vadeleri ve muayene kabul kriterlerini düzenlemektedir.',
qualificationCriteria: [
'TSE HYB Belgesi ve Yetkili Satıcı Satış Yetki Belgesi',
'ISO 13485 Medikal Cihaz Kalite Yönetim Sertifikası',
'ÜTS (Ürün Takip Sistemi) Barkod ve TİUBB Kaydı',
'En az %50 Oranında Benzer İş Deneyim Belgesi (Kamu/Özel)',
'Son 3 yıla ait Bilanço ve Gelir Tablosu oranları'
],
criticalRequirements: [
'Ürünler biyo-uyumluluk (ISO 10993) standartlarına uygun olmalıdır.',
'Teslim edilen paketlerin sterilizasyon son kullanma tarihi en az 18 ay olmalıdır.',
'Arıza durumunda 24 saat içinde yerinde teknik servis desteği sağlanmalıdır.',
'Teslimat aşamasında her parti için numune test raporu (CoA) ibraz edilmelidir.'
],
risksAndPenalties: [
{ level: 'Yüksek', clause: 'Madde 14.2', text: 'Gecikilen her takvim günü için ihale bedelinin %0.5 oranında cezai şart uygulanacaktır.' },
{ level: 'Orta', clause: 'Madde 18.1', text: 'Numune kontrolünde standart dışı bulunan partiler 3 iş günü içinde muadili ile ücretsiz değiştirilmelidir.' },
{ level: 'Düşük', clause: 'Madde 22 border', text: 'Ambalaj üzerindeki Türkçe etiket eksikliğinde kabul işlemi durdurulacaktır.' }
],
bidAnalysis: {
estimatedCost: 185000,
recommendedBid: 148500,
minBidLimit: 132000,
maxBidLimit: 165000,
winProbability: 89,
competitionLevel: 'Orta Yoğunlukta (3-4 Firma Katılımı Bekleniyor)',
historicalDiscountAvg: '%19.7 EKAP İndirim Oranı'
}
};
setAiAnalysisResults(result);
resolve(result);
}, 2200);
});
};
const [userProfile, setUserProfile] = useState({
name: 'Hikmet Tanrıverdi',
username: 'tanriverdi',
company: 'Tanrıverdi Medikal & Teknolojileri A.Ş.',
website: 'https://www.tanriverdimedikal.com.tr',
trialDaysLeft: 7,
role: 'Yönetici Tedarikçi',
scrapedCapabilities: {
scrapedAt: '2026-08-07 14:30',
extractedSectors: ['Sağlık / Tıbbi Malzeme', 'Kırtasiye & Büro', 'Temizlik & Hijyen'],
extractedProducts: ['Ortopedi Protez Setleri', 'Cerrahi Maske', 'Dezenfektan', 'Hasta Yatağı', 'Sanatsal Kırtasiye'],
extractedCertifications: ['ISO 13485 Tıbbi Cihaz Kalite', 'ISO 9001', 'ÜTS Ürün Kayıt Yetkisi', 'CE Uygunluk'],
matchScoreBoost: 15
}
});
const scanCompanyWebsite = (url) => {
return new Promise((resolve) => {
setTimeout(() => {
const domain = url.replace(/https?:\/\//, '').replace(/\/.*$/, '');
const newCapabilities = {
scrapedAt: new Date().toLocaleString('tr-TR'),
extractedSectors: ['Sağlık / Tıbbi Malzeme', 'Bilişim & Yazılım', 'Kırtasiye & Büro'],
extractedProducts: [
`${domain} - Medikal Cihaz ve Sarfları`,
'Laboratuvar Test Kiti',
'Zayıf Akım Güvenlik Sistemleri',
'Ofis & Büro Kırtasiye Malzemeleri'
],
extractedCertifications: [
'ISO 13485 Medikal Kalite',
'ISO 27001 Bilgi Güvenliği',
'ÜTS Ürün Takip Sistemi Belgesi',
'TSE Hizmet Yeterlilik Belgesi'
],
matchScoreBoost: 20
};
setUserProfile(prev => ({
...prev,
website: url,
scrapedCapabilities: newCapabilities
}));
resolve(newCapabilities);
}, 2500);
});
};
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 }
]);
// Persist state to localstorage
useEffect(() => {
localStorage.setItem('ihaleist_tenders', JSON.stringify(tenders));
}, [tenders]);
useEffect(() => {
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);
}, []);
const toggleTrackTender = (tenderId) => {
setTenders(prev => prev.map(t => {
if (t.id === tenderId) {
return { ...t, takipte: !t.takipte };
}
return t;
}));
};
const updateKanbanStatus = (tenderId, newStatus, assignedUser = null) => {
setTenders(prev => prev.map(t => {
if (t.id === tenderId) {
return {
...t,
kanbanStatus: newStatus,
atananPersonel: assignedUser !== null ? assignedUser : t.atananPersonel,
takipte: true
};
}
return t;
}));
};
const addSavedFilter = (newFilter) => {
const filterObj = {
id: 'sf-' + Date.now(),
user: userProfile.username,
...newFilter
};
setSavedFilters(prev => [filterObj, ...prev]);
};
const deleteSavedFilter = (id) => {
setSavedFilters(prev => prev.filter(f => f.id !== id));
};
const clearAllFilters = () => {
setSearchQuery('');
setSelectedCity('');
setSelectedAlimTuru('');
setSelectedKurumTipi('');
setSelectedSektor('');
setQuickFilter('all');
};
return (
<AppContext.Provider value={{
activeTab,
setActiveTab,
tenders,
setTenders,
liveStats,
searchQuery,
setSearchQuery,
selectedCity,
setSelectedCity,
selectedAlimTuru,
setSelectedAlimTuru,
selectedKurumTipi,
setSelectedKurumTipi,
selectedSektor,
setSelectedSektor,
onlyValidOnly,
setOnlyValidOnly,
quickFilter,
setQuickFilter,
savedFilters,
addSavedFilter,
deleteSavedFilter,
activeTenderDetail,
setActiveTenderDetail,
isDetailedSearchOpen,
setIsDetailedSearchOpen,
isCompanyScannerOpen,
setIsCompanyScannerOpen,
scanCompanyWebsite,
isAiSpecAnalyzerOpen,
setIsAiSpecAnalyzerOpen,
analyzeSpecPdfWithGemini,
aiAnalysisResults,
competitorWatchlist,
setCompetitorWatchlist,
vaultDocuments,
setVaultDocuments,
reminderSettings,
setReminderSettings,
webhooksConfig,
setWebhooksConfig,
apiKeys,
setApiKeys,
userProfile,
notifications,
toggleTrackTender,
updateKanbanStatus,
clearAllFilters
}}>
{children}
</AppContext.Provider>
);
};
export const useApp = () => useContext(AppContext);