Group navigation by discovery preparation market and management

This commit is contained in:
2026-09-06 13:28:31 +03:00
parent 30ce6e3a6c
commit ff3fa2c3ab
4 changed files with 50 additions and 6 deletions
+17
View File
@@ -0,0 +1,17 @@
{
"date": "2026-09-06",
"environment": "localhost:5182 normal test user",
"checks": {
"discoverVisible": true,
"prepareVisible": true,
"marketVisible": true,
"managementVisible": true,
"adminHidden": true,
"shortcutSelectsGroup": true
},
"limits": [
"Four group navigation and sidebar shortcut tested",
"No full mobile viewport or keyboard-only audit in this run",
"Admin visibility rule retained; normal-user hiding tested"
]
}
+7
View File
@@ -882,3 +882,10 @@ Taslak türü seçicisine erişilebilir ad eklendi. Üretim derlemesi geçti. Ö
- 200 otomatik test ve derleme geçti. Beş ayrı sorumlu/belge içeren taslak özeti, eski belge sürümü/depo hatası ve geçmiş deadline önceliği test edildi. - 200 otomatik test ve derleme geçti. Beş ayrı sorumlu/belge içeren taslak özeti, eski belge sürümü/depo hatası ve geçmiş deadline önceliği test edildi.
- 5182 yerel tarayıcıda beş sentetik görev tek tabloda görüldü; vergi alanına doğrudan odak, kaydedilen düzeltmenin özetten kalkması, sorumlu atamasının yansıması ve yenileme sonrası kalıcılık dahil 6 kontrol geçti (qa-preparation-overview.json). - 5182 yerel tarayıcıda beş sentetik görev tek tabloda görüldü; vergi alanına doğrudan odak, kaydedilen düzeltmenin özetten kalkması, sorumlu atamasının yansıması ve yenileme sonrası kalıcılık dahil 6 kontrol geçti (qa-preparation-overview.json).
- Bu sentetik kabul gerçek beş resmi dosyanın uygunluğunu kanıtlamaz. Firma portföyü/ürün kapasitesi, tam mobil erişilebilirlik, her eksik belgeye ayrı alt-alan odağı ve resmi çıktılarla ilgili açık maddeler devam eder. Özet kontrolleri kayıtlı taslağa dayanır; yeni AI analizi veya kaydedilmemiş değişiklikler dosya ekranında ayrıca değerlendirilir. - Bu sentetik kabul gerçek beş resmi dosyanın uygunluğunu kanıtlamaz. Firma portföyü/ürün kapasitesi, tam mobil erişilebilirlik, her eksik belgeye ayrı alt-alan odağı ve resmi çıktılarla ilgili açık maddeler devam eder. Özet kontrolleri kayıtlı taslağa dayanır; yeni AI analizi veya kaydedilmemiş değişiklikler dosya ekranında ayrıca değerlendirilir.
### D07 — Keşfet / Hazırlan / Pazar / Yönetim — 6 Eylül 2026
- Tek uzun araç şeridi dört çalışma alanına ayrıldı. Keşfet: liste/bülten/takvim/TED; Hazırlan: Kanban/doküman/KDV/sınır değer/KİK; Pazar: sonuç/sözleşme/fiyat/radar/analitik/kırmızı bayrak; Yönetim: planlar/API/PWA ve yalnız yönetici AI ayarları.
- Grup değişimi ilk aracı açar; etkin ekran ve bekleyen geçiş ilgili grubu belirler. Yan panel gibi başka yerlerden açılan ekranlarda grup otomatik uyum sağlar. Mevcut alt şerit taşma okları ve etkin aracı görünür tutma korunur. Grup düğmeleri aria-pressed, etkin araç aria-current taşır.
- Kullanım kılavuzuna grup açıklaması eklendi. Kanban yardımı yeni durum seçimi, kaydetme, hazırlık özeti ve desteklenmeyen Slack/Teams gönderimine göre güncellendi.
- Yerel normal hesapta dört grup, hazırlık panosunun açılması, normal kullanıcıda AI ayarının gizli olması ve yan panel kısayoluyla grubun değişmesi dahil 6 kontrol geçti (qa-navigation-groups.json). Derleme geçti. Önceki 200 otomatik test tekrar çalıştırılmadı; sunucu/hizmet hesabı değişmedi.
- Yeni menünün tam mobil/yalnız-klavye kabulü ve yardım metinlerinin kalan ekranlardaki iddiaları ayrıca incelenmeli. Bu değişiklik gizlenen yönetici araçlarının sunucu yetkilendirmesi yerine geçmez.
+3 -2
View File
@@ -38,8 +38,8 @@ const KILAVUZ = {
kanban: { kanban: {
baslik: 'Kanban Pano', baslik: 'Kanban Pano',
ne: 'Takipteki ihalelerin süreç panosu: İncelenecek → İnceleniyor → Teklif Hazırlanıyor → Verildi → Kazanıldı/Kaybedildi.', ne: 'Takipteki ihalelerin süreç panosu: İncelenecek → İnceleniyor → Teklif Hazırlanıyor → Verildi → Kazanıldı/Kaybedildi.',
adimlar: ['Kartları sürükleyerek kolon değiştirin.', 'İhale detayında "Hazırlık Görevi Aç" deyin: teslim tarihi ihale saati olur, kartta geri sayım başlar (BUGÜN / 2 gün / süresi geçti).', 'Derin analizden gelen belge kontrol listesi görevin içinde işaretlenir.'], adimlar: ['Kartın iş akışı durumunu seçin veya kartı sürükleyin. Kayıt tamamlanana kadar bekleyin; hata varsa işlem kaydedilmiş sayılmaz.', 'İhale detayında "Hazırlık Görevi Aç" ile dosyayı panoya ekleyin; sorumluyu karttan atayın.', 'Hazırlık dosyaları özetinde eksik belge ve sıradaki kontrolü açın. Eksik bağlantısı dosyanın ilgili hazırlık alanına götürür; değişikliği taslağa kaydedince özet güncellenir.'],
ipuclari: ['Görev açılmış ihale sonuçlanınca bildirim gelir: kim, kaça, %kaç kırım.'], ipuclari: ['Kontrol sayısı kayıtlı taslağa dayanır ve resmi başvuru onayı değildir. Belge durumunu yenile düğmesiyle depo kayıtlarını tekrar kontrol edin.', 'Slack/Teams gönderimi bu panoda desteklenmez.'],
}, },
rakip_radar: { baslik: 'Rakip Radar', ne: 'Kazanan firmalar, kırım oranı, ortalama teklif sayısı, tek teklifle kazanma oranı.', adimlar: ['"Radara Ekle" ile izleyin; teklif verince/kazanınca uyarı alın.', '"⛔ yasaklı sorgula" KİK sorgusunu açar, firma adını panoya kopyalar.'], ipuclari: ['Kırım oranı yalnız kısımsız ihalelerde hesaplanır — kısmi ihalede sayı yanıltıcı olurdu.'] }, rakip_radar: { baslik: 'Rakip Radar', ne: 'Kazanan firmalar, kırım oranı, ortalama teklif sayısı, tek teklifle kazanma oranı.', adimlar: ['"Radara Ekle" ile izleyin; teklif verince/kazanınca uyarı alın.', '"⛔ yasaklı sorgula" KİK sorgusunu açar, firma adını panoya kopyalar.'], ipuclari: ['Kırım oranı yalnız kısımsız ihalelerde hesaplanır — kısmi ihalede sayı yanıltıcı olurdu.'] },
dokuman_deposu: { dokuman_deposu: {
@@ -127,6 +127,7 @@ export const KullanimKilavuzu = () => {
<button onClick={() => setAcik(false)} className="text-slate-400 hover:text-white p-1" aria-label="Kapat"><X className="w-5 h-5" /></button> <button onClick={() => setAcik(false)} className="text-slate-400 hover:text-white p-1" aria-label="Kapat"><X className="w-5 h-5" /></button>
</div> </div>
<div className="flex-1 overflow-y-auto p-5 space-y-5"> <div className="flex-1 overflow-y-auto p-5 space-y-5">
<p className="text-xs text-slate-600">Üst menüde Keşfet ilanları ve takvimi, Hazırlan dosyaları ve hesap araçlarını, Pazar sonuçları ve analizleri, Yönetim hesap ve bağlantı ayarlarını açar. AI model ayarları yalnız yöneticilere görünür.</p>
<p className="text-xs text-slate-700 leading-relaxed bg-slate-50 border border-slate-200 rounded-xl p-3">{k.ne}</p> <p className="text-xs text-slate-700 leading-relaxed bg-slate-50 border border-slate-200 rounded-xl p-3">{k.ne}</p>
<Bolum icon={ListChecks} renk="text-blue-600" baslik="Nasıl kullanılır" satirlar={k.adimlar} sirali /> <Bolum icon={ListChecks} renk="text-blue-600" baslik="Nasıl kullanılır" satirlar={k.adimlar} sirali />
<Bolum icon={Lightbulb} renk="text-amber-500" baslik="İpuçları" satirlar={k.ipuclari} /> <Bolum icon={Lightbulb} renk="text-amber-500" baslik="İpuçları" satirlar={k.ipuclari} />
+23 -4
View File
@@ -27,6 +27,13 @@ const RENK = {
}; };
const RENK_VARSAYILAN = { ikon: 'text-slate-500', aktif: 'border-blue-200 bg-blue-50 text-blue-700', hover: 'hover:bg-slate-50' }; const RENK_VARSAYILAN = { ikon: 'text-slate-500', aktif: 'border-blue-200 bg-blue-50 text-blue-700', hover: 'hover:bg-slate-50' };
const NAV_GROUPS = [
{id:'discover',label:'Keşfet',tabs:['list','bulten','takvim','ted_eu']},
{id:'prepare',label:'Hazırlan',tabs:['kanban','dokuman_deposu','kdv','sinir','kik_kararlari']},
{id:'market',label:'Pazar',tabs:['sonuc','sozlesme','fiyat','rakip_radar','analitik_dashboard','kirmizi_bayrak']},
{id:'manage',label:'Yönetim',tabs:['planlar','api_webhooks','pwa_settings','ai_ayarlari']},
];
export const TopNavigation = () => { export const TopNavigation = () => {
const { activeTab, setActiveTab, isAdmin } = useApp(); const { activeTab, setActiveTab, isAdmin } = useApp();
@@ -66,6 +73,9 @@ export const TopNavigation = () => {
{ id: 'pwa_settings', label: 'Mobil PWA', icon: Smartphone } { id: 'pwa_settings', label: 'Mobil PWA', icon: Smartphone }
]; ];
const activeGroup=NAV_GROUPS.find(group=>group.tabs.includes(bekleyen || activeTab)) || NAV_GROUPS[0];
const visibleItems=activeGroup.tabs.map(id=>navItems.find(item=>item.id===id)).filter(Boolean);
// 18 sekme çoğu ekrana sığmıyor. Kaydırma çubuğu gizli olduğu için sondaki // 18 sekme çoğu ekrana sığmıyor. Kaydırma çubuğu gizli olduğu için sondaki
// sekmeler (Planlar, API & Webhooks, Mobil PWA) görünmez kalıyordu; taşma // sekmeler (Planlar, API & Webhooks, Mobil PWA) görünmez kalıyordu; taşma
// olduğunda ok düğmeleri gösterilir ve etkin sekme görünür alana kaydırılır. // olduğunda ok düğmeleri gösterilir ve etkin sekme görünür alana kaydırılır.
@@ -97,7 +107,8 @@ export const TopNavigation = () => {
useEffect(() => { useEffect(() => {
const el = seritRef.current?.querySelector(`[data-tab="${activeTab}"]`); const el = seritRef.current?.querySelector(`[data-tab="${activeTab}"]`);
el?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' }); el?.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'center' });
}, [activeTab]); tasmaHesapla();
}, [activeTab, activeGroup.id, tasmaHesapla]);
const kaydir = (yon) => { const kaydir = (yon) => {
seritRef.current?.scrollBy({ left: yon * 280, behavior: 'smooth' }); seritRef.current?.scrollBy({ left: yon * 280, behavior: 'smooth' });
@@ -105,6 +116,13 @@ export const TopNavigation = () => {
return ( return (
<div className="bg-white border-b border-slate-200 shadow-xs sticky top-16 z-30"> <div className="bg-white border-b border-slate-200 shadow-xs sticky top-16 z-30">
<nav aria-label="Çalışma alanları" className="max-w-[1600px] mx-auto px-4 pt-2 flex flex-wrap gap-2">
{NAV_GROUPS.map(group=><button key={group.id} type="button" aria-pressed={group.id===activeGroup.id}
onClick={()=>{if(group.id!==activeGroup.id)sekmeSec(group.tabs.find(id=>navItems.some(item=>item.id===id)));}}
className={`px-4 py-2 rounded-lg text-sm font-bold border ${group.id===activeGroup.id ? 'bg-slate-900 text-white border-slate-900' : 'border-slate-200 text-slate-700 hover:bg-slate-50'}`}>
{group.label}
</button>)}
</nav>
<div className="max-w-[1600px] mx-auto px-4 relative"> <div className="max-w-[1600px] mx-auto px-4 relative">
{tasma.sol && ( {tasma.sol && (
<button <button
@@ -126,8 +144,8 @@ export const TopNavigation = () => {
<ChevronRight className="w-4 h-4" /> <ChevronRight className="w-4 h-4" />
</button> </button>
)} )}
<div ref={seritRef} className="flex items-center gap-1 overflow-x-auto py-1 scrollbar-none scroll-smooth"> <nav aria-label={`${activeGroup.label} araçları`} ref={seritRef} className="flex items-center gap-1 overflow-x-auto py-1 scrollbar-none scroll-smooth">
{navItems.map(item => { {visibleItems.map(item => {
const Icon = item.icon; const Icon = item.icon;
const renk = RENK[item.id] || RENK_VARSAYILAN; const renk = RENK[item.id] || RENK_VARSAYILAN;
const isActive = bekleyen ? bekleyen === item.id : activeTab === item.id; const isActive = bekleyen ? bekleyen === item.id : activeTab === item.id;
@@ -136,6 +154,7 @@ export const TopNavigation = () => {
<button <button
key={item.id} key={item.id}
data-tab={item.id} data-tab={item.id}
aria-current={isActive ? 'page' : undefined}
aria-busy={yukleniyor || undefined} aria-busy={yukleniyor || undefined}
onClick={() => sekmeSec(item.id)} onClick={() => sekmeSec(item.id)}
className={`flex items-center gap-2 px-3.5 py-2 rounded-lg border text-xs font-semibold whitespace-nowrap transition-all select-none ${ className={`flex items-center gap-2 px-3.5 py-2 rounded-lg border text-xs font-semibold whitespace-nowrap transition-all select-none ${
@@ -156,7 +175,7 @@ export const TopNavigation = () => {
</button> </button>
); );
})} })}
</div> </nav>
</div> </div>
</div> </div>
); );