fix: Sidebar scrollbar + einklappbare Gruppen

This commit is contained in:
2026-06-28 13:18:23 +02:00
parent 93807f7672
commit 0ec55acfc5

View File

@@ -158,6 +158,9 @@ function Login({ onLogin }) {
function Sidebar({ active, setActive, user, onLogout, unreadMsgs=0 }) { function Sidebar({ active, setActive, user, onLogout, unreadMsgs=0 }) {
const [appsOpen, setAppsOpen] = useState(true); const [appsOpen, setAppsOpen] = useState(true);
const [collapsed, setCollapsed] = useState(false); const [collapsed, setCollapsed] = useState(false);
const [collapsedGroups, setCollapsedGroups] = useState({});
const toggleGroup = (group) => setCollapsedGroups(prev => ({ ...prev, [group]: !prev[group] }));
const NavBtn = ({ item, sub=false }) => { const NavBtn = ({ item, sub=false }) => {
const ic = active===item.id ? '#4ecdc4' : 'rgba(255,255,255,0.55)'; const ic = active===item.id ? '#4ecdc4' : 'rgba(255,255,255,0.55)';
@@ -220,7 +223,7 @@ function Sidebar({ active, setActive, user, onLogout, unreadMsgs=0 }) {
</div> </div>
)} )}
</div> </div>
<nav style={{ flex:1, padding:'0 8px' }}> <nav style={{ flex:1, padding:'0 8px', overflowY:'auto', overflowX:'hidden' }}>
<NavBtn item={{ id:'dashboard', Icon:HomeIcon, label:'Dashboard' }} /> <NavBtn item={{ id:'dashboard', Icon:HomeIcon, label:'Dashboard' }} />
{/* Apps Sektion mit Gruppenüberschriften */} {/* Apps Sektion mit Gruppenüberschriften */}
@@ -237,13 +240,26 @@ function Sidebar({ active, setActive, user, onLogout, unreadMsgs=0 }) {
{!collapsed && <span style={{ flex:1, color:'rgba(255,255,255,0.65)' }}>Apps</span>} {!collapsed && <span style={{ flex:1, color:'rgba(255,255,255,0.65)' }}>Apps</span>}
{!collapsed && <ChevronIcon size={13} color="rgba(255,255,255,0.35)" dir={appsOpen?'down':'right'}/>} {!collapsed && <ChevronIcon size={13} color="rgba(255,255,255,0.35)" dir={appsOpen?'down':'right'}/>}
</button> </button>
{appsOpen && getGroupedTools().map(([group, tools]) => ( {appsOpen && getGroupedTools().map(([group, tools]) => {
const isGroupCollapsed = !!collapsedGroups[group];
return (
<div key={group}> <div key={group}>
{!collapsed && <div style={{ color:'rgba(78,205,196,0.85)', fontSize:9, fontFamily:'monospace', {!collapsed && (
letterSpacing:2, padding:'8px 12px 3px 12px' }}>{group.toUpperCase()}</div>} <button onClick={() => toggleGroup(group)} style={{
{tools.map(t => <NavBtn key={t.id} item={t} sub />)} width:'100%', display:'flex', alignItems:'center', gap:4,
background:'none', border:'none', cursor:'pointer',
padding:'8px 12px 3px 12px', marginBottom:0,
}}>
<span style={{ color:'rgba(78,205,196,0.85)', fontSize:9, fontFamily:'monospace', letterSpacing:2, flex:1, textAlign:'left' }}>
{group.toUpperCase()}
</span>
<ChevronIcon size={10} color="rgba(78,205,196,0.5)" dir={isGroupCollapsed ? 'right' : 'down'} />
</button>
)}
{!isGroupCollapsed && tools.map(t => <NavBtn key={t.id} item={t} sub />)}
</div> </div>
))} );
})}
<NavBtn item={{ id:'admin', Icon:AdminIcon, label:'Einstellungen' }} /> <NavBtn item={{ id:'admin', Icon:AdminIcon, label:'Einstellungen' }} />
</nav> </nav>
@@ -1552,7 +1568,6 @@ function SearchResultItem({ item, idx, activeIdx, onNavigate, onHover }) {
if (item.type==='calendar_feed') { icon='📅'; primary=item.name; secondary='Kalender-Abo'; } if (item.type==='calendar_feed') { icon='📅'; primary=item.name; secondary='Kalender-Abo'; }
if (item.type==='calendar_event') { icon='📆'; primary=item.summary; secondary=`${item.feed_name||'Kalender'}${item.start_dt?' · '+formatIcalDate(item.start_dt):''}`; } if (item.type==='calendar_event') { icon='📆'; primary=item.summary; secondary=`${item.feed_name||'Kalender'}${item.start_dt?' · '+formatIcalDate(item.start_dt):''}`; }
if (item.type==='qr') { icon='◻'; primary=item.label||item.url; secondary=`QR-Code · ${item.url}`; } if (item.type==='qr') { icon='◻'; primary=item.label||item.url; secondary=`QR-Code · ${item.url}`; }
if (item.type==='geo') { icon='🗺️'; primary=`vs ${item.opp_name||item.owner_name}`; secondary=item.status==='active'?'Gebietseroberung · laufend':'Gebietseroberung · beendet'; }
return ( return (
<div onClick={()=>onNavigate(item)} onMouseEnter={()=>onHover(idx)} <div onClick={()=>onNavigate(item)} onMouseEnter={()=>onHover(idx)}
style={{display:'flex',alignItems:'center',gap:10,padding:'9px 14px',cursor:'pointer', style={{display:'flex',alignItems:'center',gap:10,padding:'9px 14px',cursor:'pointer',
@@ -1589,7 +1604,6 @@ const SEARCH_TOOL_INDEX = [
{type:'tool',label:'Whiteboard',icon:'🖊',sub:'Werkzeuge',id:'whiteboard',keywords:['whiteboard','zeichnen','canvas','board','draw','skizze','malen','zeichen']}, {type:'tool',label:'Whiteboard',icon:'🖊',sub:'Werkzeuge',id:'whiteboard',keywords:['whiteboard','zeichnen','canvas','board','draw','skizze','malen','zeichen']},
{type:'tool',label:'Dev-Tools',icon:'🔧',sub:'Werkzeuge',id:'devtools',keywords:['entwickler','developer','tools','werkzeuge']}, {type:'tool',label:'Dev-Tools',icon:'🔧',sub:'Werkzeuge',id:'devtools',keywords:['entwickler','developer','tools','werkzeuge']},
{type:'tool',label:'Paywall-Killer',icon:'🔓',sub:'Werkzeuge',id:'paywallkiller',keywords:['paywall','archiv','archive','artikel','zeitung','bild','waz','heise','spiegel','zeit','bypass','umgehen','lesen','gesperrt','bezahlschranke']}, {type:'tool',label:'Paywall-Killer',icon:'🔓',sub:'Werkzeuge',id:'paywallkiller',keywords:['paywall','archiv','archive','artikel','zeitung','bild','waz','heise','spiegel','zeit','bypass','umgehen','lesen','gesperrt','bezahlschranke']},
{type:'tool',label:'Hex Wars',icon:'⬡',sub:'Freizeit',id:'gebietseroberung',keywords:['hex wars','spiel','game','multiplayer','gebiet','strategie','raster','einladen','duell','gebietseroberung']},
{type:'tool',label:'Media',icon:'🎬',sub:'Freizeit',id:'media',keywords:['kino','film','movie','streaming','demnächst','favoriten','cinema']}, {type:'tool',label:'Media',icon:'🎬',sub:'Freizeit',id:'media',keywords:['kino','film','movie','streaming','demnächst','favoriten','cinema']},
{type:'tool',label:'Kino Aktuell',icon:'🎬',sub:'Media',id:'media',keywords:['kino','kinocharts','charts','laufend','now playing']}, {type:'tool',label:'Kino Aktuell',icon:'🎬',sub:'Media',id:'media',keywords:['kino','kinocharts','charts','laufend','now playing']},
{type:'tool',label:'Kino Demnächst',icon:'🗓',sub:'Media',id:'media',keywords:['demnächst','neustart','upcoming','vorschau','kino']}, {type:'tool',label:'Kino Demnächst',icon:'🗓',sub:'Media',id:'media',keywords:['demnächst','neustart','upcoming','vorschau','kino']},
@@ -1743,7 +1757,6 @@ function GlobalSearch({ setActive, setDevToolsNav, mobile }) {
else if (item.type === 'push') { setActive('dashboard'); } else if (item.type === 'push') { setActive('dashboard'); }
else if (item.type === 'calendar_feed' || item.type === 'calendar_event') { setActive('dashboard'); } else if (item.type === 'calendar_feed' || item.type === 'calendar_event') { setActive('dashboard'); }
else if (item.type === 'qr') { setActive('devtools'); setDevToolsNav({tool:'qrcode', ts: Date.now()}); } else if (item.type === 'qr') { setActive('devtools'); setDevToolsNav({tool:'qrcode', ts: Date.now()}); }
else if (item.type === 'geo') { setActive('gebietseroberung'); }
}; };
const onKey = e => { const onKey = e => {
@@ -1904,12 +1917,6 @@ function GlobalSearch({ setActive, setDevToolsNav, mobile }) {
<div> <div>
<div style={{padding:'7px 14px 4px',color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:9,letterSpacing:1}}>KALENDER-EINTRÄGE</div> <div style={{padding:'7px 14px 4px',color:'rgba(255,255,255,0.3)',fontFamily:'monospace',fontSize:9,letterSpacing:1}}>KALENDER-EINTRÄGE</div>
{results.calendar_events.map((item,i)=>(<SearchResultItem key={`ce${i}`} item={{...item,type:'calendar_event'}} idx={0} activeIdx={-1} onNavigate={navigate} onHover={()=>{}}/>))} {results.calendar_events.map((item,i)=>(<SearchResultItem key={`ce${i}`} item={{...item,type:'calendar_event'}} idx={0} activeIdx={-1} onNavigate={navigate} onHover={()=>{}}/>))}
{results.geo_games?.length>0 && (
<>
<div style={secHead}>GEBIETSEROBERUNG</div>
{results.geo_games.map((item,i)=>(<SearchResultItem key={`geo${i}`} item={{...item,type:'geo'}} idx={0} activeIdx={-1} onNavigate={navigate} onHover={()=>{}}/>))}
</>
)}
</div> </div>
)} )}
{results.calendar_feeds?.length>0 && ( {results.calendar_feeds?.length>0 && (