Files
ihaleist/src/components/RightSidebar.jsx
T

175 lines
8.6 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 { YaklasanSonTarihler } from './YaklasanSonTarihler';
import { firmStats, companyStats } from '../services/analytics';
import { Briefcase, FileText, Calendar, History, Trophy, Eye, Kanban, Radar, ArrowRight, Users, Plus, Trash2 } from 'lucide-react';
export const RightSidebar = () => {
const { setActiveTab, setQuickFilter, tenders, teamMembers, teamSaving, teamError, addTeamMember, removeTeamMember } = useApp();
const [tmName, setTmName] = useState('');
const [tmEmail, setTmEmail] = useState('');
const [tmRole, setTmRole] = useState('Uzman');
const cs = companyStats(tenders);
const topFirms = firmStats(tenders).firms.slice(0, 3);
const handleAddMember = async (e) => {
e.preventDefault();
if (!tmName.trim()) return;
const saved=await addTeamMember({ name: tmName.trim(), email: tmEmail.trim(), role: tmRole });
if(saved){setTmName('');setTmEmail('');}
};
const records = [
{ label: 'İncelediklerim', count: cs.reviewed, icon: History, filterKey: 'incelenenler' },
{ label: 'Sözleşmelerim', count: cs.contracts, icon: FileText, filterKey: 'sozlesmeler' },
{ label: 'Sonuçlarım', count: cs.resulted, icon: Trophy, filterKey: 'sonuclananlar' },
{ label: 'Takibimdekiler', count: cs.tracked, icon: Eye, filterKey: 'takiptekiler' }
];
return (
<aside className="w-full lg:w-80 shrink-0 space-y-4">
{/* Firmanıza Ait Kayıtlar */}
<div className="bg-white rounded-xl border border-slate-200 shadow-xs overflow-hidden">
<div className="px-4 py-3 border-b border-slate-100 bg-slate-50 flex items-center justify-between">
<span className="font-bold text-xs uppercase tracking-wider text-slate-800 flex items-center gap-2">
<Briefcase className="w-4 h-4 text-blue-600" /> Firmanıza Ait Kayıtlar
</span>
</div>
<div className="p-2 space-y-1">
{records.map(rec => {
const Icon = rec.icon;
return (
<div
key={rec.label}
onClick={() => {
setQuickFilter(rec.filterKey);
setActiveTab('list');
}}
className="flex items-center justify-between p-2.5 rounded-lg hover:bg-slate-50 cursor-pointer transition-colors text-xs font-semibold text-slate-700"
>
<div className="flex items-center gap-2.5">
<Icon className="w-4 h-4 text-slate-400" />
<span>{rec.label}</span>
</div>
<span className="bg-slate-100 text-slate-800 font-bold px-2 py-0.5 rounded-md border border-slate-200">
{rec.count}
</span>
</div>
);
})}
</div>
</div>
{/* Yaklaşan ihale tarihleri — takip/panodaki ihaleler, 14 gün */}
<YaklasanSonTarihler />
{/* İhale Durumlarım (Kanban Entrance) */}
<div className="bg-white rounded-xl border border-slate-200 shadow-xs p-4 space-y-3">
<div className="flex items-center gap-2 font-bold text-xs uppercase tracking-wider text-slate-800 border-b border-slate-100 pb-2">
<Kanban className="w-4 h-4 text-blue-600" /> İhale Durumlarım
</div>
<p className="text-xs text-slate-500 leading-relaxed">
Takibe aldığınız ihalelere durum atayabilir, ekip içinde sürükle-bırak ile iş dağıtabilirsiniz.
</p>
<button
onClick={() => setActiveTab('kanban')}
className="w-full bg-[#1f2937] hover:bg-slate-800 text-white font-bold py-2.5 px-4 rounded-lg text-xs flex items-center justify-center gap-2 transition-colors shadow-xs"
>
<Kanban className="w-4 h-4 text-amber-400" />
<span>Kanban Panosunu Aç</span>
</button>
</div>
{/* Rakip Radar Widget */}
<div className="bg-white rounded-xl border border-slate-200 shadow-xs p-4 space-y-3">
<div className="flex items-center justify-between border-b border-slate-100 pb-2">
<span className="font-bold text-xs uppercase tracking-wider text-slate-800 flex items-center gap-2">
<Radar className="w-4 h-4 text-indigo-600" /> Rakip Radar & Pazar Payı
</span>
<span className="bg-indigo-100 text-indigo-800 text-[10px] font-bold px-2 py-0.5 rounded-full">
Sektör Analizi
</span>
</div>
<div className="space-y-2.5 text-xs">
{topFirms.length === 0 ? (
<p className="text-[11px] text-slate-400 py-2 leading-relaxed">
Kazanan firma verisi biriktikçe (doğrudan temin & sonuç ilanları) gerçek pazar payları burada listelenir.
</p>
) : (
topFirms.map((comp, i) => (
<div key={i} className="space-y-1">
<div className="flex items-center justify-between text-[11px] font-semibold text-slate-700">
<span className="truncate max-w-[170px]">{comp.name}</span>
<span className="font-bold text-indigo-900">%{comp.share} ({comp.wins} İhale)</span>
</div>
<div className="w-full bg-slate-100 rounded-full h-1.5 overflow-hidden">
<div
className="bg-indigo-600 h-1.5 rounded-full"
style={{ width: `${Math.max(comp.share, 3)}%` }}
/>
</div>
</div>
))
)}
</div>
<button
onClick={() => setActiveTab('sozlesme')}
className="w-full text-indigo-600 hover:text-indigo-800 font-bold text-xs py-1.5 flex items-center justify-center gap-1 hover:underline"
>
<span>Tüm Rakip Sözleşmelerini İncele</span>
<ArrowRight className="w-3.5 h-3.5" />
</button>
</div>
{/* Ekip Yönetimi — sunucu kaydı */}
<div className="bg-white rounded-2xl border border-slate-200 shadow-xs p-4 space-y-3">
<h3 className="font-extrabold text-xs uppercase tracking-wider text-slate-800 flex items-center gap-2">
<Users className="w-4 h-4 text-indigo-600" /> Ekip Yönetimi ({teamMembers.length})
</h3>
<div className="space-y-1.5 max-h-40 overflow-y-auto">
{teamMembers.map((m) => (
<div key={m.id} className="flex items-center justify-between gap-2 bg-slate-50 rounded-lg px-2.5 py-1.5 border border-slate-100">
<div className="min-w-0">
<div className="text-xs font-bold text-slate-800 truncate">{m.name}</div>
<div className="text-[10px] text-slate-500 truncate">{m.role}{m.email ? ` · ${m.email}` : ''}</div>
</div>
{(
<button disabled={teamSaving} aria-label={`${m.name} ekip kaydını çıkar`} onClick={() => removeTeamMember(m.id)} className="text-slate-400 hover:text-red-500 shrink-0" title="Çıkar">
<Trash2 className="w-3.5 h-3.5" />
</button>
)}
</div>
))}
</div>
{teamError&&<p role="alert" className="text-xs text-red-700">{teamError}</p>}
{teamSaving&&<p role="status" className="text-xs">Ekip kaydediliyor…</p>}
<form onSubmit={handleAddMember} className="space-y-1.5 pt-1 border-t border-slate-100">
<fieldset disabled={teamSaving} className="space-y-1.5">
<input aria-label="Ekip üyesi adı" maxLength={200} value={tmName} onChange={(e) => setTmName(e.target.value)} placeholder="Ad Soyad"
className="w-full bg-slate-50 border border-slate-200 rounded-lg px-2.5 py-1.5 text-xs" />
<div className="flex gap-1.5">
<input aria-label="Ekip üyesi e-postası" type="email" maxLength={254} value={tmEmail} onChange={(e) => setTmEmail(e.target.value)} placeholder="E-posta (ops.)"
className="flex-1 bg-slate-50 border border-slate-200 rounded-lg px-2.5 py-1.5 text-xs" />
<select aria-label="Ekip üyesi görevi" value={tmRole} onChange={(e) => setTmRole(e.target.value)}
className="bg-slate-50 border border-slate-200 rounded-lg px-2 py-1.5 text-xs">
<option>Uzman</option><option>Yönetici</option><option>Saha</option><option>Muhasebe</option>
</select>
</div>
<button type="submit" disabled={teamSaving || !tmName.trim()} className="w-full bg-indigo-600 hover:bg-indigo-500 text-white font-bold text-xs py-2 rounded-lg flex items-center justify-center gap-1">
<Plus className="w-3.5 h-3.5" /> Ekip Üyesi Ekle
</button>
</fieldset>
</form>
</div>
</aside>
);
};