feat(ui): introduce shared PageSidebar for Settings and Admin

Replaces the inline tab bar on SettingsPage and AdminPage with a
responsive sidebar layout (left nav on desktop, hamburger drawer on
mobile). Each tab gets a lucide-react icon for quick scanning. Both
pages drop max-w-6xl so the panel fills the viewport.
This commit is contained in:
Maurice
2026-04-19 21:35:31 +02:00
parent 0d534f13cf
commit 4f3368502a
3 changed files with 267 additions and 68 deletions
+32 -36
View File
@@ -1,10 +1,11 @@
import React, { useState, useEffect } from 'react'
import { useSearchParams } from 'react-router-dom'
import { Settings } from 'lucide-react'
import { Settings, Palette, Map, Bell, Plug, CloudOff, User, Info } from 'lucide-react'
import { useTranslation } from '../i18n'
import { authApi } from '../api/client'
import { useAddonStore } from '../store/addonStore'
import Navbar from '../components/Layout/Navbar'
import PageSidebar, { type PageSidebarTab } from '../components/Layout/PageSidebar'
import DisplaySettingsTab from '../components/Settings/DisplaySettingsTab'
import MapSettingsTab from '../components/Settings/MapSettingsTab'
import NotificationsTab from '../components/Settings/NotificationsTab'
@@ -37,14 +38,18 @@ export default function SettingsPage(): React.ReactElement {
}
}, [searchParams])
const TABS = [
{ id: 'display', label: t('settings.tabs.display') },
{ id: 'map', label: t('settings.tabs.map') },
{ id: 'notifications', label: t('settings.tabs.notifications') },
...(hasIntegrations ? [{ id: 'integrations', label: t('settings.tabs.integrations') }] : []),
{ id: 'offline', label: t('settings.tabs.offline') },
{ id: 'account', label: t('settings.tabs.account') },
...(appVersion ? [{ id: 'about', label: t('settings.tabs.about') }] : []),
const tabs: PageSidebarTab[] = [
{ id: 'display', label: t('settings.tabs.display'), icon: Palette },
{ id: 'map', label: t('settings.tabs.map'), icon: Map },
{ id: 'notifications', label: t('settings.tabs.notifications'), icon: Bell },
...(hasIntegrations
? [{ id: 'integrations', label: t('settings.tabs.integrations'), icon: Plug }]
: []),
{ id: 'offline', label: t('settings.tabs.offline'), icon: CloudOff },
{ id: 'account', label: t('settings.tabs.account'), icon: User },
...(appVersion
? [{ id: 'about', label: t('settings.tabs.about'), icon: Info }]
: []),
]
return (
@@ -52,7 +57,7 @@ export default function SettingsPage(): React.ReactElement {
<Navbar />
<div style={{ paddingTop: 'var(--nav-h)' }}>
<div className="max-w-6xl mx-auto px-4 py-8">
<div className="w-full px-4 sm:px-6 lg:px-8 py-8">
{/* Header */}
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl flex items-center justify-center" style={{ background: 'var(--bg-tertiary)' }}>
@@ -64,33 +69,24 @@ export default function SettingsPage(): React.ReactElement {
</div>
</div>
{/* Tab bar */}
<div className="grid grid-cols-3 sm:flex gap-1 mb-6 rounded-xl p-1" style={{ background: 'var(--bg-card)', border: '1px solid var(--border-primary)' }}>
{TABS.map(tab => (
<button
key={tab.id}
onClick={() => setActiveTab(tab.id)}
className={`px-3 sm:px-4 py-2 text-xs sm:text-sm font-medium rounded-lg transition-colors ${
activeTab === tab.id
? 'bg-slate-900 text-white'
: 'text-slate-600 hover:text-slate-900 hover:bg-slate-50'
}`}
>
{tab.label}
</button>
))}
</div>
{/* Tab content */}
{activeTab === 'display' && <DisplaySettingsTab />}
{activeTab === 'map' && <MapSettingsTab />}
{activeTab === 'notifications' && <NotificationsTab />}
{activeTab === 'integrations' && hasIntegrations && <IntegrationsTab />}
{activeTab === 'offline' && <OfflineTab />}
{activeTab === 'account' && <AccountTab />}
{activeTab === 'about' && appVersion && <AboutTab appVersion={appVersion} />}
{/* Sidebar layout */}
<PageSidebar
sidebarLabel={t('settings.title').toUpperCase()}
tabs={tabs}
activeTab={activeTab}
onTabChange={setActiveTab}
footer={appVersion ? `v${appVersion} · self-hosted` : 'self-hosted'}
>
{activeTab === 'display' && <DisplaySettingsTab />}
{activeTab === 'map' && <MapSettingsTab />}
{activeTab === 'notifications' && <NotificationsTab />}
{activeTab === 'integrations' && hasIntegrations && <IntegrationsTab />}
{activeTab === 'offline' && <OfflineTab />}
{activeTab === 'account' && <AccountTab />}
{activeTab === 'about' && appVersion && <AboutTab appVersion={appVersion} />}
</PageSidebar>
</div>
</div>
</div>
)
}
}