refactor(admin): group the admin sidebar tabs into sections

The admin sidebar had 11 flat tabs. PageSidebar now supports optional group headings (backward-compatible; the Settings sidebar stays flat), and the admin tabs are grouped into Users, Configuration, Integrations and Maintenance. Group labels added across all locales.
This commit is contained in:
Maurice
2026-06-29 13:27:05 +02:00
committed by Maurice
parent 4d131db9af
commit 1cc69fc22a
23 changed files with 136 additions and 34 deletions
+37 -23
View File
@@ -5,6 +5,9 @@ export interface PageSidebarTab {
id: string id: string
label: string label: string
icon: LucideIcon icon: LucideIcon
/** Optional group heading shown above the first tab of each group. Tabs that
* share a group must be contiguous in the array. */
group?: string
} }
interface PageSidebarProps { interface PageSidebarProps {
@@ -160,29 +163,40 @@ function SidebarInner({
</div> </div>
)} )}
<nav className="flex flex-col gap-1 flex-1"> <nav className="flex flex-col gap-1 flex-1">
{tabs.map((tab) => { {(() => {
const Icon = tab.icon let lastGroup: string | undefined
const active = tab.id === activeTab return tabs.map((tab) => {
return ( const Icon = tab.icon
<button const active = tab.id === activeTab
key={tab.id} const showHeader = !!tab.group && tab.group !== lastGroup
onClick={() => onTabChange(tab.id)} lastGroup = tab.group
className={`flex items-center gap-2.5 px-3 py-2 rounded-lg text-sm text-left transition-colors ${active ? 'text-content font-semibold' : 'text-content-secondary font-medium'}`} return (
style={{ <React.Fragment key={tab.id}>
background: active ? 'var(--bg-hover)' : 'transparent', {showHeader && (
}} <div className="text-[10px] font-bold tracking-widest uppercase text-content-faint px-3 mt-3 mb-0.5 first:mt-0">
onMouseEnter={(e) => { {tab.group}
if (!active) e.currentTarget.style.background = 'var(--bg-hover)' </div>
}} )}
onMouseLeave={(e) => { <button
if (!active) e.currentTarget.style.background = 'transparent' onClick={() => onTabChange(tab.id)}
}} className={`flex items-center gap-2.5 px-3 py-2 rounded-lg text-sm text-left transition-colors ${active ? 'text-content font-semibold' : 'text-content-secondary font-medium'}`}
> style={{
<Icon size={16} className="shrink-0" /> background: active ? 'var(--bg-hover)' : 'transparent',
<span className="truncate">{tab.label}</span> }}
</button> onMouseEnter={(e) => {
) if (!active) e.currentTarget.style.background = 'var(--bg-hover)'
})} }}
onMouseLeave={(e) => {
if (!active) e.currentTarget.style.background = 'transparent'
}}
>
<Icon size={16} className="shrink-0" />
<span className="truncate">{tab.label}</span>
</button>
</React.Fragment>
)
})
})()}
</nav> </nav>
{footer && ( {footer && (
<div <div
+15 -11
View File
@@ -35,18 +35,22 @@ export default function AdminPage(): React.ReactElement {
updateInfo, setShowUpdateModal, updateInfo, setShowUpdateModal,
} = admin } = admin
const gUsers = t('admin.group.users')
const gConfig = t('admin.group.config')
const gIntegration = t('admin.group.integration')
const gMaintenance = t('admin.group.maintenance')
const TABS: PageSidebarTab[] = [ const TABS: PageSidebarTab[] = [
{ id: 'users', label: t('admin.tabs.users'), icon: Users }, { id: 'users', label: t('admin.tabs.users'), icon: Users, group: gUsers },
{ id: 'config', label: t('admin.tabs.config'), icon: SlidersHorizontal }, { id: 'defaults', label: t('admin.tabs.defaults'), icon: UserCog, group: gUsers },
{ id: 'defaults', label: t('admin.tabs.defaults'), icon: UserCog }, { id: 'config', label: t('admin.tabs.config'), icon: SlidersHorizontal, group: gConfig },
{ id: 'addons', label: t('admin.tabs.addons'), icon: Puzzle }, { id: 'settings', label: t('admin.tabs.settings'), icon: SettingsIcon, group: gConfig },
{ id: 'settings', label: t('admin.tabs.settings'), icon: SettingsIcon }, { id: 'addons', label: t('admin.tabs.addons'), icon: Puzzle, group: gConfig },
{ id: 'notifications', label: t('admin.tabs.notifications'), icon: Bell }, { id: 'notifications', label: t('admin.tabs.notifications'), icon: Bell, group: gIntegration },
{ id: 'backup', label: t('admin.tabs.backup'), icon: Database }, ...(mcpEnabled ? [{ id: 'mcp-tokens', label: t('admin.tabs.mcpTokens'), icon: KeyRound, group: gIntegration }] : []),
{ id: 'audit', label: t('admin.tabs.audit'), icon: ScrollText }, { id: 'github', label: t('admin.tabs.github'), icon: GitBranch, group: gIntegration },
...(mcpEnabled ? [{ id: 'mcp-tokens', label: t('admin.tabs.mcpTokens'), icon: KeyRound }] : []), { id: 'backup', label: t('admin.tabs.backup'), icon: Database, group: gMaintenance },
{ id: 'github', label: t('admin.tabs.github'), icon: GitBranch }, { id: 'audit', label: t('admin.tabs.audit'), icon: ScrollText, group: gMaintenance },
...(devMode ? [{ id: 'dev-notifications', label: 'Dev: Notifications', icon: Bug }] : []), ...(devMode ? [{ id: 'dev-notifications', label: 'Dev: Notifications', icon: Bug, group: gMaintenance }] : []),
] ]
return ( return (
+4
View File
@@ -343,5 +343,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'اختر نمطًا…', 'admin.defaultSettings.mapboxStylePlaceholder': 'اختر نمطًا…',
'admin.defaultSettings.mapbox3d': 'المباني والتضاريس ثلاثية الأبعاد', 'admin.defaultSettings.mapbox3d': 'المباني والتضاريس ثلاثية الأبعاد',
'admin.defaultSettings.mapboxQuality': 'وضع الجودة العالية', 'admin.defaultSettings.mapboxQuality': 'وضع الجودة العالية',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -350,5 +350,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Escolha um estilo…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Escolha um estilo…',
'admin.defaultSettings.mapbox3d': 'Edifícios & relevo em 3D', 'admin.defaultSettings.mapbox3d': 'Edifícios & relevo em 3D',
'admin.defaultSettings.mapboxQuality': 'Modo de alta qualidade', 'admin.defaultSettings.mapboxQuality': 'Modo de alta qualidade',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -348,5 +348,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Vyberte styl…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Vyberte styl…',
'admin.defaultSettings.mapbox3d': '3D budovy & terén', 'admin.defaultSettings.mapbox3d': '3D budovy & terén',
'admin.defaultSettings.mapboxQuality': 'Režim vysoké kvality', 'admin.defaultSettings.mapboxQuality': 'Režim vysoké kvality',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -353,5 +353,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Stil auswählen…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Stil auswählen…',
'admin.defaultSettings.mapbox3d': '3D-Gebäude & Gelände', 'admin.defaultSettings.mapbox3d': '3D-Gebäude & Gelände',
'admin.defaultSettings.mapboxQuality': 'Hochqualitätsmodus', 'admin.defaultSettings.mapboxQuality': 'Hochqualitätsmodus',
'admin.group.users': 'Benutzer',
'admin.group.config': 'Konfiguration',
'admin.group.integration': 'Integrationen',
'admin.group.maintenance': 'Wartung',
}; };
export default admin; export default admin;
+4
View File
@@ -348,5 +348,9 @@ const admin: TranslationStrings = {
"Remove all of this user's passkeys (e.g. on a lost device). They can still sign in with their password.", "Remove all of this user's passkeys (e.g. on a lost device). They can still sign in with their password.",
'admin.passkey.resetConfirm': 'Remove all passkeys for {name}?', 'admin.passkey.resetConfirm': 'Remove all passkeys for {name}?',
'admin.passkey.resetDone': 'Removed {count} passkey(s)', 'admin.passkey.resetDone': 'Removed {count} passkey(s)',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -359,5 +359,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Elige un estilo…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Elige un estilo…',
'admin.defaultSettings.mapbox3d': 'Edificios y terreno en 3D', 'admin.defaultSettings.mapbox3d': 'Edificios y terreno en 3D',
'admin.defaultSettings.mapboxQuality': 'Modo de alta calidad', 'admin.defaultSettings.mapboxQuality': 'Modo de alta calidad',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -356,5 +356,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Choisissez un style…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Choisissez un style…',
'admin.defaultSettings.mapbox3d': 'Bâtiments & terrain en 3D', 'admin.defaultSettings.mapbox3d': 'Bâtiments & terrain en 3D',
'admin.defaultSettings.mapboxQuality': 'Mode haute qualité', 'admin.defaultSettings.mapboxQuality': 'Mode haute qualité',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -363,5 +363,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Επιλέξτε ένα στυλ…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Επιλέξτε ένα στυλ…',
'admin.defaultSettings.mapbox3d': 'Κτίρια & ανάγλυφο 3D', 'admin.defaultSettings.mapbox3d': 'Κτίρια & ανάγλυφο 3D',
'admin.defaultSettings.mapboxQuality': 'Λειτουργία υψηλής ποιότητας', 'admin.defaultSettings.mapboxQuality': 'Λειτουργία υψηλής ποιότητας',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -355,5 +355,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Válassz stílust…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Válassz stílust…',
'admin.defaultSettings.mapbox3d': '3D épületek & domborzat', 'admin.defaultSettings.mapbox3d': '3D épületek & domborzat',
'admin.defaultSettings.mapboxQuality': 'Kiváló minőségű mód', 'admin.defaultSettings.mapboxQuality': 'Kiváló minőségű mód',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -351,5 +351,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Pilih gaya…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Pilih gaya…',
'admin.defaultSettings.mapbox3d': 'Bangunan & medan 3D', 'admin.defaultSettings.mapbox3d': 'Bangunan & medan 3D',
'admin.defaultSettings.mapboxQuality': 'Mode kualitas tinggi', 'admin.defaultSettings.mapboxQuality': 'Mode kualitas tinggi',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -354,5 +354,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Scegli uno stile…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Scegli uno stile…',
'admin.defaultSettings.mapbox3d': 'Edifici & terreno in 3D', 'admin.defaultSettings.mapbox3d': 'Edifici & terreno in 3D',
'admin.defaultSettings.mapboxQuality': 'Modalità alta qualità', 'admin.defaultSettings.mapboxQuality': 'Modalità alta qualità',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -339,5 +339,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'スタイルを選択…', 'admin.defaultSettings.mapboxStylePlaceholder': 'スタイルを選択…',
'admin.defaultSettings.mapbox3d': '3D の建物と地形', 'admin.defaultSettings.mapbox3d': '3D の建物と地形',
'admin.defaultSettings.mapboxQuality': '高品質モード', 'admin.defaultSettings.mapboxQuality': '高品質モード',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -342,5 +342,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': '스타일을 선택하세요…', 'admin.defaultSettings.mapboxStylePlaceholder': '스타일을 선택하세요…',
'admin.defaultSettings.mapbox3d': '3D 건물 & 지형', 'admin.defaultSettings.mapbox3d': '3D 건물 & 지형',
'admin.defaultSettings.mapboxQuality': '고품질 모드', 'admin.defaultSettings.mapboxQuality': '고품질 모드',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -354,5 +354,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Kies een stijl…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Kies een stijl…',
'admin.defaultSettings.mapbox3d': '3D-gebouwen & terrein', 'admin.defaultSettings.mapbox3d': '3D-gebouwen & terrein',
'admin.defaultSettings.mapboxQuality': 'Hogekwaliteitsmodus', 'admin.defaultSettings.mapboxQuality': 'Hogekwaliteitsmodus',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -358,5 +358,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Wybierz styl…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Wybierz styl…',
'admin.defaultSettings.mapbox3d': 'Budynki i teren 3D', 'admin.defaultSettings.mapbox3d': 'Budynki i teren 3D',
'admin.defaultSettings.mapboxQuality': 'Tryb wysokiej jakości', 'admin.defaultSettings.mapboxQuality': 'Tryb wysokiej jakości',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -353,5 +353,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Выберите стиль…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Выберите стиль…',
'admin.defaultSettings.mapbox3d': '3D-здания и рельеф', 'admin.defaultSettings.mapbox3d': '3D-здания и рельеф',
'admin.defaultSettings.mapboxQuality': 'Режим высокого качества', 'admin.defaultSettings.mapboxQuality': 'Режим высокого качества',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -348,5 +348,9 @@ const admin: TranslationStrings = {
"Ta bort alla den här användarens inloggningsnycklar (t.ex. om enheten har försvunnit). Användaren kan fortfarande logga in med sitt lösenord.", "Ta bort alla den här användarens inloggningsnycklar (t.ex. om enheten har försvunnit). Användaren kan fortfarande logga in med sitt lösenord.",
'admin.passkey.resetConfirm': 'Ta bort alla åtkomstnycklar för {name}?', 'admin.passkey.resetConfirm': 'Ta bort alla åtkomstnycklar för {name}?',
'admin.passkey.resetDone': 'Tog bort {count} inloggningsnycklar', 'admin.passkey.resetDone': 'Tog bort {count} inloggningsnycklar',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -357,5 +357,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Bir stil seçin…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Bir stil seçin…',
'admin.defaultSettings.mapbox3d': '3D binalar & arazi', 'admin.defaultSettings.mapbox3d': '3D binalar & arazi',
'admin.defaultSettings.mapboxQuality': 'Yüksek kalite modu', 'admin.defaultSettings.mapboxQuality': 'Yüksek kalite modu',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -353,5 +353,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': 'Виберіть стиль…', 'admin.defaultSettings.mapboxStylePlaceholder': 'Виберіть стиль…',
'admin.defaultSettings.mapbox3d': '3D-будівлі та рельєф', 'admin.defaultSettings.mapbox3d': '3D-будівлі та рельєф',
'admin.defaultSettings.mapboxQuality': 'Режим високої якості', 'admin.defaultSettings.mapboxQuality': 'Режим високої якості',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -335,5 +335,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': '選擇樣式…', 'admin.defaultSettings.mapboxStylePlaceholder': '選擇樣式…',
'admin.defaultSettings.mapbox3d': '3D 建築物與地形', 'admin.defaultSettings.mapbox3d': '3D 建築物與地形',
'admin.defaultSettings.mapboxQuality': '高品質模式', 'admin.defaultSettings.mapboxQuality': '高品質模式',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;
+4
View File
@@ -333,5 +333,9 @@ const admin: TranslationStrings = {
'admin.defaultSettings.mapboxStylePlaceholder': '选择一种样式…', 'admin.defaultSettings.mapboxStylePlaceholder': '选择一种样式…',
'admin.defaultSettings.mapbox3d': '3D 建筑与地形', 'admin.defaultSettings.mapbox3d': '3D 建筑与地形',
'admin.defaultSettings.mapboxQuality': '高质量模式', 'admin.defaultSettings.mapboxQuality': '高质量模式',
'admin.group.users': 'Users',
'admin.group.config': 'Configuration',
'admin.group.integration': 'Integrations',
'admin.group.maintenance': 'Maintenance',
}; };
export default admin; export default admin;