feat: demo özelliklerini gerçekleştir — canlı veri + localStorage
- analytics.js: firmStats/itemStats/provinceDensity (gerçek hesaplama) - Rakip Radar: canlı veriden kazanan firma analizi (mock 3 firma kaldırıldı) - Fiyat Analizi: gerçek kalem min/medyan/ort/maks + CSV export - TurkeyHeatMap: 81 il gerçek interaktif yoğunluk haritası (tıkla→filtrele) - Favori kurumlar: kart yıldızı + hızlı filtre (localStorage) - Ekip yönetimi: gerçek CRUD (RightSidebar) + Kanban ataması ekipten - Bildirimler: gerçek Notification API + izin + test + sessiz saatler - PDF: jsPDF ile gerçek birim fiyat teklif cetveli (kaşe/imza alanı)
This commit is contained in:
@@ -113,6 +113,35 @@ export const AppProvider = ({ children }) => {
|
||||
{ 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 }
|
||||
]);
|
||||
|
||||
// --- Favori kurumlar (localStorage, gerçek) ---
|
||||
const [favoriteKurumlar, setFavoriteKurumlar] = useState(() => {
|
||||
try { return JSON.parse(localStorage.getItem('ihaleist_favori_kurumlar') || '[]'); } catch { return []; }
|
||||
});
|
||||
useEffect(() => {
|
||||
localStorage.setItem('ihaleist_favori_kurumlar', JSON.stringify(favoriteKurumlar));
|
||||
}, [favoriteKurumlar]);
|
||||
const toggleFavoriteKurum = (kurum) => {
|
||||
const ad = (kurum || '').trim();
|
||||
if (!ad) return;
|
||||
setFavoriteKurumlar((prev) => (prev.includes(ad) ? prev.filter((k) => k !== ad) : [...prev, ad]));
|
||||
};
|
||||
|
||||
// --- Ekip yönetimi (localStorage, gerçek CRUD) ---
|
||||
const [teamMembers, setTeamMembers] = useState(() => {
|
||||
try {
|
||||
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' },
|
||||
];
|
||||
});
|
||||
useEffect(() => {
|
||||
localStorage.setItem('ihaleist_ekip', JSON.stringify(teamMembers));
|
||||
}, [teamMembers]);
|
||||
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' },
|
||||
@@ -121,16 +150,32 @@ export const AppProvider = ({ children }) => {
|
||||
{ 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 [reminderSettings, setReminderSettings] = useState(() => {
|
||||
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(() => {
|
||||
localStorage.setItem('ihaleist_reminder', JSON.stringify(reminderSettings));
|
||||
}, [reminderSettings]);
|
||||
|
||||
// Şu an sessiz saat aralığında mıyız (gece yarısını geçen aralıkları da destekler)
|
||||
const isQuietNow = () => {
|
||||
if (!reminderSettings.quietEnabled) return false;
|
||||
const now = new Date();
|
||||
const cur = now.getHours() * 60 + now.getMinutes();
|
||||
const [sh, sm] = reminderSettings.quietStart.split(':').map(Number);
|
||||
const [eh, em] = reminderSettings.quietEnd.split(':').map(Number);
|
||||
const start = sh * 60 + sm, end = eh * 60 + em;
|
||||
return start <= end ? cur >= start && cur < end : cur >= start || cur < end;
|
||||
};
|
||||
|
||||
const [webhooksConfig, setWebhooksConfig] = useState({
|
||||
slackWebhookUrl: 'https://hooks.slack.com/services/T00/B00/XXXXXX',
|
||||
@@ -279,6 +324,24 @@ export const AppProvider = ({ children }) => {
|
||||
{ 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 }
|
||||
]);
|
||||
|
||||
// Gerçek bildirim: listeye ekler; sessiz saat dışında ve push açıksa tarayıcı
|
||||
// bildirimi (Notification API) da gösterir.
|
||||
const notify = (title, text) => {
|
||||
setNotifications((prev) => [{ id: 'n-' + Date.now(), title, text, time: 'az önce', read: false }, ...prev].slice(0, 30));
|
||||
if (isQuietNow() || !reminderSettings.pushEnabled) return;
|
||||
try {
|
||||
if ('Notification' in window && Notification.permission === 'granted') {
|
||||
new Notification(title, { body: text, icon: '/favicon.ico' });
|
||||
}
|
||||
} catch { /* yoksay */ }
|
||||
};
|
||||
const requestPushPermission = async () => {
|
||||
if (!('Notification' in window)) return 'unsupported';
|
||||
if (Notification.permission === 'granted') return 'granted';
|
||||
try { return await Notification.requestPermission(); } catch { return 'denied'; }
|
||||
};
|
||||
const markNotificationsRead = () => setNotifications((prev) => prev.map((n) => ({ ...n, read: true })));
|
||||
|
||||
// Persist state to localstorage
|
||||
useEffect(() => {
|
||||
localStorage.setItem('ihaleist_tenders', JSON.stringify(tenders));
|
||||
@@ -410,6 +473,15 @@ export const AppProvider = ({ children }) => {
|
||||
activeAiModel,
|
||||
userProfile,
|
||||
notifications,
|
||||
notify,
|
||||
requestPushPermission,
|
||||
markNotificationsRead,
|
||||
isQuietNow,
|
||||
favoriteKurumlar,
|
||||
toggleFavoriteKurum,
|
||||
teamMembers,
|
||||
addTeamMember,
|
||||
removeTeamMember,
|
||||
toggleTrackTender,
|
||||
updateKanbanStatus,
|
||||
clearAllFilters
|
||||
|
||||
Reference in New Issue
Block a user