Files
ihaleist/src/components/Header.jsx
T

245 lines
12 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useState } from 'react';
import { useApp } from '../context/AppContext';
import { Package, Bot, Search, SlidersHorizontal, Gift, Bell, User, ChevronDown, CheckCircle, LogOut, Globe, Sparkles, RefreshCw } from 'lucide-react';
export const Header = () => {
const {
searchQuery,
setSearchQuery,
setIsDetailedSearchOpen,
setIsUrunAramaOpen,
setIsCompanyScannerOpen,
setIsAiSpecAnalyzerOpen,
userProfile,
notifications,
setActiveTab,
clearAllFilters,
isRefreshingLive,
refreshLiveEKAPData,
authUser,
isAdmin,
plan,
handleLogout
} = useApp();
// Rozet SATIN ALINAN aboneliği gösterir. Yönetici hesapları her özelliğe
// erişir ama bu bir abonelik değildir; ikisini karıştırmamak için ayrı yazılır.
const planAdi = plan?.abonelikVar ? plan.abonelikAdi : (plan?.yonetici ? 'Yönetici' : 'Ücretsiz');
const [showNotifications, setShowNotifications] = useState(false);
const [showUserMenu, setShowUserMenu] = useState(false);
const handleSearchSubmit = (e) => {
e.preventDefault();
setActiveTab('list');
};
return (
<header className="bg-[#1f2937] text-white border-b border-slate-700 sticky top-0 z-40 shadow-sm">
<div className="max-w-[1600px] mx-auto px-3 sm:px-4 py-2 xl:py-0 xl:h-16 flex flex-wrap xl:flex-nowrap items-center justify-between gap-2 sm:gap-4">
{/* Logo */}
<div
onClick={() => { setActiveTab('list'); clearAllFilters(); }}
className="flex shrink-0 items-center gap-1.5 sm:gap-2.5 cursor-pointer select-none group"
>
<div className="w-8 h-8 sm:w-10 sm:h-10 rounded-xl bg-blue-600 flex items-center justify-center shadow-md group-hover:bg-blue-500 transition-colors">
<Bot className="w-6 h-6 text-white" />
</div>
<div className="flex flex-col">
<span className="font-black text-xl sm:text-2xl tracking-tight text-white flex items-center gap-1">
İhale<span className="text-blue-400 font-extrabold">ist</span>
</span>
</div>
</div>
{/* Search Bar */}
<form onSubmit={handleSearchSubmit} className="order-3 xl:order-none w-full xl:w-auto xl:flex-1 min-w-0 xl:max-w-2xl flex items-center gap-2">
<div className="relative flex-1 min-w-0">
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="İhale, kurum veya İKN no ile arayın..."
className="w-full bg-[#374151] text-white placeholder-slate-400 pl-4 pr-10 py-2 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 border border-slate-600 transition-all"
/>
{searchQuery && (
<button
type="button"
onClick={() => setSearchQuery('')}
className="absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 hover:text-white text-xs"
>
✕
</button>
)}
</div>
<button
type="submit"
className="bg-slate-700 hover:bg-slate-600 text-white font-medium px-4 py-2 rounded-lg text-sm flex items-center gap-1.5 transition-colors border border-slate-600 shrink-0"
>
<Search className="w-4 h-4" />
<span className="font-semibold text-xs tracking-wider">ARA</span>
</button>
<button
type="button"
data-product-search-trigger="true"
onClick={() => setIsUrunAramaOpen(true)}
title="Ürün Arama — ürün adınızı ihale dokümanlarının içinde arar"
className="bg-amber-400 hover:bg-amber-300 text-slate-950 font-black px-3 py-2 rounded-lg text-xs flex items-center gap-1.5 transition-colors shrink-0"
>
<Package className="w-4 h-4" />
<span className="hidden sm:inline tracking-wide">ÜRÜN ARA</span>
</button>
<button
type="button"
onClick={() => setIsDetailedSearchOpen(true)}
title="Detaylı İhale Arama"
className="bg-blue-600 hover:bg-blue-500 text-white p-2 rounded-lg text-sm flex items-center justify-center transition-colors shadow-sm shrink-0"
>
<SlidersHorizontal className="w-4 h-4" />
</button>
</form>
{/* Right Action Icons */}
<div className="ml-auto flex items-center gap-1 sm:gap-3 shrink-0">
{/* Real Live EKAP Mode Badge */}
<button
onClick={refreshLiveEKAPData}
disabled={isRefreshingLive}
className="bg-emerald-600 hover:bg-emerald-500 text-white font-extrabold px-3 py-1.5 rounded-lg text-xs hidden md:hidden md:flex items-center gap-1.5 shadow-xs transition-colors shrink-0 disabled:opacity-50"
title="Canlı EKAP ve Doğrudan Temin Verilerini Anlık Güncelleyin"
>
<RefreshCw className={`w-3.5 h-3.5 text-white ${isRefreshingLive ? 'animate-spin' : ''}`} />
<span className="hidden lg:inline">{isRefreshingLive ? 'EKAP Çekiliyor...' : '🔴 CANLI EKAP VERİSİ'}</span>
</button>
{/* AI Şartname Analizi */}
<button
onClick={() => setIsAiSpecAnalyzerOpen(true)}
className="bg-purple-600 hover:bg-purple-500 text-white font-extrabold px-3 py-1.5 rounded-lg text-xs flex items-center gap-1.5 shadow-xs transition-colors shrink-0"
title="Teknik şartnameyi AI ile analiz edin"
>
<Sparkles className="w-4 h-4 text-amber-300" />
<span className="hidden md:inline">AI Şartname Analizi</span>
</button>
{/* Company Web Scanner Button */}
<button
onClick={() => setIsCompanyScannerOpen(true)}
className="bg-blue-600 hover:bg-blue-500 text-white font-bold px-3 py-1.5 rounded-lg text-xs hidden md:flex items-center gap-1.5 shadow-xs transition-colors shrink-0"
title="Web Sitenizi Tarayın & AI İhale Profiline İşleyin"
>
<Globe className="w-4 h-4 text-blue-200" />
<span className="hidden sm:inline">Web Siteni Tara (AI)</span>
</button>
{/* Trial Badge */}
<div className="bg-amber-400 text-slate-900 font-bold px-3 py-1.5 rounded-lg text-xs flex items-center gap-1.5 shadow-sm">
<Gift className="w-4 h-4 text-slate-900" />
<span>{userProfile.trialDaysLeft} gün</span>
</div>
{/* Notifications Dropdown */}
<div className="relative">
<button
aria-label="Bildirimler"
onClick={() => setShowNotifications(!showNotifications)}
className="relative p-2 text-slate-300 hover:text-white hover:bg-slate-700 rounded-lg transition-colors"
>
<Bell className="w-5 h-5" />
{notifications.some(n => !n.read) && (
<span className="absolute top-1 right-1 w-2.5 h-2.5 bg-blue-500 rounded-full ring-2 ring-[#1f2937]" />
)}
</button>
{showNotifications && (
<div className="absolute right-0 mt-2 w-80 bg-white text-slate-800 rounded-xl shadow-xl border border-slate-200 py-2 z-50 animate-fade-in">
<div className="px-4 py-2 border-b border-slate-100 flex items-center justify-between">
<h4 className="font-bold text-sm">Bildirimler</h4>
<span className="text-xs text-blue-600 cursor-pointer hover:underline">Tümünü Okundu İşaretle</span>
</div>
<div className="max-h-64 overflow-y-auto divide-y divide-slate-100">
{notifications.map(n => (
<div key={n.id} className="p-3 hover:bg-slate-50 transition-colors cursor-pointer text-xs">
<div className="font-semibold text-slate-900 flex justify-between">
<span>{n.title}</span>
<span className="text-slate-400 font-normal text-[10px]">{n.time}</span>
</div>
<p className="text-slate-600 mt-1 leading-relaxed">{n.text}</p>
</div>
))}
</div>
</div>
)}
</div>
{/* User Profile */}
<div className="relative">
<button
aria-label="Hesap menüsü"
onClick={() => setShowUserMenu(!showUserMenu)}
className="bg-slate-700 hover:bg-slate-600 text-white px-3 py-1.5 rounded-lg text-xs font-semibold flex items-center gap-2 transition-colors border border-slate-600"
>
{authUser?.picture ? (
<img src={authUser.picture} alt="" className="w-6 h-6 rounded-full object-cover" referrerPolicy="no-referrer" />
) : (
<div className="w-6 h-6 rounded-full bg-blue-600 flex items-center justify-center text-white font-bold text-xs">
{(authUser?.name || userProfile.username).charAt(0).toUpperCase()}
</div>
)}
<span className="hidden sm:inline">{(authUser?.name || userProfile.username).split(' ')[0]}</span>
<ChevronDown className="w-3.5 h-3.5 text-slate-400" />
</button>
{showUserMenu && (
<div className="absolute right-0 mt-2 w-60 bg-white text-slate-800 rounded-xl shadow-xl border border-slate-200 py-2 z-50 animate-fade-in">
<div className="px-4 py-2 border-b border-slate-100">
<p className="font-bold text-sm text-slate-900 flex items-center gap-1.5">
{authUser?.name || userProfile.name}
{isAdmin && <span className="text-[9px] font-black px-1.5 py-0.5 rounded bg-indigo-600 text-white">ADMIN</span>}
</p>
<p className="text-xs text-slate-500 truncate">{authUser?.email || userProfile.company}</p>
</div>
<div className="py-1 text-xs">
<button
onClick={() => { setShowUserMenu(false); setActiveTab('list'); }}
className="w-full text-left px-4 py-2 hover:bg-slate-50 flex items-center gap-2 text-slate-700"
>
<User className="w-4 h-4 text-slate-400" /> Firma Bilgileri & Üyeler
</button>
<button
onClick={() => { setShowUserMenu(false); setActiveTab('planlar'); }}
className="w-full text-left px-4 py-2 hover:bg-slate-50 flex items-center gap-2 text-slate-700"
>
<Gift className="w-4 h-4 text-amber-500" />
<span>Abonelik & Planlar</span>
<span className="ml-auto text-[10px] font-bold px-1.5 py-0.5 rounded-full bg-slate-100 text-slate-600 border border-slate-200">
{planAdi}
</span>
</button>
<button
onClick={() => { setShowUserMenu(false); setActiveTab('pwa_settings'); }}
className="w-full text-left px-4 py-2 hover:bg-slate-50 flex items-center gap-2 text-slate-700"
>
<CheckCircle className="w-4 h-4 text-slate-400" /> Bildirim Tercihleri
</button>
</div>
<div className="border-t border-slate-100 pt-1 text-xs">
<button
onClick={handleLogout}
className="w-full text-left px-4 py-2 text-red-600 hover:bg-red-50 flex items-center gap-2"
>
<LogOut className="w-4 h-4" /> Çıkış Yap
</button>
</div>
</div>
)}
</div>
</div>
</div>
</header>
);
};