diff --git a/docs/navigation-audit.md b/docs/navigation-audit.md
index 78d97856..7837d581 100644
--- a/docs/navigation-audit.md
+++ b/docs/navigation-audit.md
@@ -37,7 +37,10 @@ That fix is the template for the HIGH items below. **Caveat:** the fix only did
**Existing entity routes:** `/chat/:sessionId`, `/jobs/:id`, `/dashboards/:id`, `/projects/:id`, `/email/:emailId`.
**Flat screens:** `/` `/files` `/music` `/soulseek` `/tasks` `/skills` `/processes` `/activity`
-`/system-monitor` `/task-logs` `/plans` `/terminal` `/desktop` `/browser` `/code-editor` `/settings/{profile,ai,system,integrations,apps}`.
+`/system-monitor` `/task-logs` `/plans` `/terminal` `/desktop` `/browser` `/code-editor`.
+The five settings pages are route **pairs** now, not flat screens — `/settings/{profile,ai,system,integrations,user-management}`
+plus a `:section` each (M6). There has never been a `/settings/apps`; that entry was wrong when this list
+was written.
**What's already correct** (lean on these in the refactor): the **Dock**, **Header** (logo + mobile sheet),
**UserMenu**, **JobsIndicator** are all real ` `s. Shared `NavLink.tsx` (query-string-appending ` `
@@ -72,9 +75,9 @@ are good building blocks. The **Workspace/Panel framework** contains **zero** ro
| M3 | `Screens/Dashboard/Activity/ActivityScreen.tsx:63,73` | background task / detached job | `/activity/:id` | two row types; unify under one param, screen re-derives `task=`/`path=`. |
| M4 | `FileBrowser/.../useFileBrowserApp.ts:269`, `FileItem.tsx:516`, `Breadcrumb.tsx:16`, search-hit `:249` | a folder | `/files?path=
` | **files** already open via `?view=`; **folders** are pure `currentPath` state — no URL, no back/forward. Folder rows + crumbs → ` ` on a `?path=` param. |
| M5 | `CodeEditor/FileTree.tsx:59`, `EditorTabs.tsx:33` | open source file / active tab | `/code-editor?file=` (+ `open=` for tabs) | tree file rows → ` `; active file in a param. |
-| M6 | `Layout/Settings/SettingsPanel.tsx:39` | a settings sub-section | `/settings/:page/:section` or `?section=` | `` → `useGlobal`; nothing in URL, not deep-linkable. **One change covers all settings pages** (all go through `createSettingsPanelComponents`). |
+| ~~M6~~ | `Settings/SettingsPanel.tsx` | a settings sub-section | `/settings/:page/:section` | **Done.** `` + `useParams`, five `*_SELECTED` globals gone, one `SettingsRoute` guard per page. The "one change covers all settings pages" claim was *almost* right: Integrations builds its own sidebar and did not go through `createSettingsPanelComponents`, and it also held the Enterprise/Personal tab in a second global — derived from the section key now, which is what fixes deep-linking a Personal section. |
| M7 | `workspaces/components/Combobox.tsx:53` | caller-supplied route | (existing) | shared widget: `href`-bearing options do `navigate(option.href)`; render them as ` `. Every caller inherits the opaque click. |
-| M8 | `Layout/Header/UserMenu.tsx:51` | — | — | **dead target:** ` ` — route doesn't exist (falls through to `/`). Add the route or remove the item. |
+| ~~M8~~ | `Layout/Header/UserMenu.tsx` | — | — | **Done.** Removed rather than routed: nothing had ever been built behind `/settings/resources`, so the item was a bounce to `/` dressed as navigation. Its `header.userMenu.resources` locale keys went with it. |
| M9 | `Screens/Dashboard/Plans/index.tsx:36` | a plan document | `/plans/:name` | native `` → local state; a plan is a real addressable doc. Have the select `navigate()` or use a link list. |
| M10 | `SystemMonitor/ScopeList.tsx:14` | monitor scope (btop/pm2/docker) | `/system-monitor/:scope` | small fixed set; low urgency but genuinely addressable. |
@@ -120,7 +123,6 @@ This is the primary surface to convert to URL-driven selection.
| `soulseek:section` | active Soulseek section | `/soulseek/:section` | `SoulseekNav/View/Dashboard/UserMenu` |
| `soulseek:user` | selected peer | `/soulseek/users/:name` | `UserMenu.tsx:28`, `SoulseekUsers.tsx:34` |
| `monitor:scope` | monitor scope | `/system-monitor/:scope` | `ScopeList.tsx` (via `shared.ts:15`) |
-| settings `useGlobal(*_SELECTED)` | settings section | `/settings/:page/:section` | `SettingsPanel.tsx:56` (M6) |
| local `useState` selections | search / room / conversation / peer / open file / preview slug | respective nested routes | `SearchView`, `SoulseekRooms`, `SoulseekChat`, `SoulseekUsers`, `CodeEditor/useEditorState`, `PreviewProvider` |
**Excluded — event-bus / refresh signals, NOT selection:** `files:refresh-signal`,
@@ -186,11 +188,11 @@ publishers means changing the chat panel, which is another agent's, so it is wri
- [x] **H3** Projects rows → ` `; `SELECTED_PROJECT` global replaced by `?selected=` URL param across `ProjectListApp`/`ProjectListScreen`/`ProjectPreview`. Done — `2aaacc8`. **Needs runtime test.**
- [ ] **H4** Chat detail: read `sessionId` from `useParams`, retire `chat:selected-session` as source of truth (`ChatDetailPanel.tsx:136`) — **finishes the /chat fix**. *Deferred: overlaps the in-flight `sidecars-*` chat-comms work; do after that lands.*
- [ ] **H5** Email rows → ` ` driven by `useParams().emailId`; delete the state↔URL sync effects (`EmailList.tsx:336`, `EmailScreen.tsx:27-33`).
-- [ ] **M8** Fix or remove the dead `/settings/resources` link (`UserMenu.tsx:51`).
+- [x] **M8** Dead `/settings/resources` menu item removed from `UserMenu.tsx`, along with its now-orphaned `en`/`pt` locale keys. **Needs runtime test.**
- [ ] Verify + convert the 4 preview "open" navigates (`DashboardPreview:355,377`, `ProjectPreview:430,457`); leave genuine post-mutation redirects.
### Phase 2 — Add a route, then link (per-entity, medium effort)
-- [ ] **M6** Settings sub-sections → nested route or `?section=`, render `SectionButton` as `` (`SettingsPanel.tsx:39`) — one change covers all settings pages.
+- [x] **M6** Settings sub-sections → `/settings/:page/:section`; `SectionButton` is now a `SectionLink` (``), the five `*_SELECTED` globals and `INTEGRATIONS_SETTINGS_TAB` are gone, and each page renders one `SettingsRoute` guard that canonicalises the bare route and a bogus section. **Needs runtime test.**
- [ ] **M1** Capabilities → `/tasks|skills|processes/:dir`, rows → ` ` (`CapabilityPage.tsx:431`) — covers 3 screens.
- [ ] **M2** TaskLogs → `/task-logs/:id` (`TaskLogs/index.tsx:104`).
- [ ] **M3** Activity → `/activity/:id` (`ActivityScreen.tsx:63,73`).
diff --git a/docs/workspace-panel-todo.md b/docs/workspace-panel-todo.md
index 39fa1f18..121e8a02 100644
--- a/docs/workspace-panel-todo.md
+++ b/docs/workspace-panel-todo.md
@@ -634,6 +634,19 @@ All the same bug: an app guessing "am I being closed?" from an unmount, or payin
(`frontend.tsx:15-21`) with no per-route scoping, and `reset` is called nowhere.
Authority: `docs/navigation-audit.md`.
+ **Settings is done** (audit M6). Five pages moved to `/settings/:page/:section`: the section is
+ `useParams`, the sidebar entry is a `` instead of a `` with the key in its closure,
+ and each page renders one `SettingsRoute` guard that canonicalises the bare route and an unknown
+ section. Six globals went with it — the five `*_SELECTED` keys and `INTEGRATIONS_SETTINGS_TAB`.
+ Two things the doc's "one fix point" framing had wrong. Integrations does **not** go through
+ `createSettingsPanelComponents` — it builds its own sidebar, so it needed its own conversion. And
+ its Enterprise/Personal tab was a *second* global, which is why deep-linking a Personal section
+ could never have worked: the link set the section, the tab stayed on Enterprise, and the content
+ pane rendered "Select a section" for a section that existed. The tab is derived from the section
+ key now, so there is nothing left to disagree with.
+ Remaining here: Music, Soulseek, SystemMonitor, Email, Browser. **Chat detail
+ (`chat:selected-session`) is chat-owned — leave it; it is written up in `COMMS/`.**
+
### 5.9 The context has grown an app-config section — _(found 2026-08-07)_
`WorkspaceContext` is 18 fields, of which the framework itself reads none of the first six. Apps never
diff --git a/src/apps/officer-web/App.tsx b/src/apps/officer-web/App.tsx
index 8729a895..1ea1d920 100644
--- a/src/apps/officer-web/App.tsx
+++ b/src/apps/officer-web/App.tsx
@@ -30,11 +30,20 @@ export function App() {
} />
+ {/* Route pairs, like /headscale and /photos below: the bare page route exists so the
+ existing links into it keep working, and its `SettingsRoute` guard redirects to the
+ first section. The section is the URL rather than a `*_SELECTED` global, so a settings
+ sub-page is linkable, bookmarkable and cmd-clickable. */}
} />
+ } />
} />
+ } />
} />
+ } />
} />
+ } />
} />
+ } />
{/* A project group is a working directory, so it is spelled as a path, not ?cwd= — see
apps/ChatHistory/chat-routes.ts. The splat has to be last in a pattern, which is why
"new" comes before the group rather than after it. A session carries no group: the
diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/Header/UserMenu.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/Header/UserMenu.tsx
index 02b7c8e5..e28e1e2a 100644
--- a/src/apps/officer-web/Screens/Dashboard/Layout/Header/UserMenu.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Layout/Header/UserMenu.tsx
@@ -1,7 +1,7 @@
import { Link } from 'react-router';
import * as Dropdown from '@/components/ui/dropdown-menu';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
-import { User, LogOut, Settings, Package, Puzzle, Sun, Moon, Bot, UserCog } from 'lucide-react';
+import { User, LogOut, Settings, Puzzle, Sun, Moon, Bot, UserCog } from 'lucide-react';
import { useAuth } from 'hooks/useAuth';
import { useTranslation } from '@/lib/i18n';
import { useColorMode } from '@/components/ui/ThemeProvider';
@@ -47,12 +47,9 @@ export function UserMenu() {
{t('header.userMenu.systemSettings')}
-
-
-
- {t('header.userMenu.resources')}
-
-
+ {/* No Resources entry. `/settings/resources` has never been a route, so this item sent you to
+ the catch-all and out to `/` — a menu entry that looks like navigation and is a bounce.
+ (navigation-audit M8.) */}
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/AISettings.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/AISettings.tsx
index d3e0197f..1b85b0bd 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/AISettings.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/AISettings.tsx
@@ -7,7 +7,7 @@ import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceLayout } from 'officerdev';
import { useAuth } from 'hooks/useAuth';
-import { createSettingsPanelComponents, type SettingsSectionGroup } from './SettingsPanel';
+import { SettingsRoute, createSettingsPanelComponents, type SettingsSectionGroup } from './SettingsPanel';
import { useSettings } from 'state/useSettings';
import type { UserSettings } from 'state/useSettings';
import { useModels, useVisibleModels, modelKey, getProviderDisplayName, type ModelOption } from 'state/useModels';
@@ -462,6 +462,8 @@ function useAISettingsGroups(): SettingsSectionGroup[] {
}, []);
}
+const BASE_PATH = '/settings/ai';
+
const layout: LayoutNode = {
type: 'group',
id: 'ai-root',
@@ -475,10 +477,10 @@ const layout: LayoutNode = {
export const AISettings = () => {
const groups = useAISettingsGroups();
- const { Sidebar, Content } = useMemo(
+ const { Sidebar, Content, allSections } = useMemo(
() =>
createSettingsPanelComponents({
- globalKey: 'AI_SETTINGS_SELECTED',
+ basePath: BASE_PATH,
sidebarIcon: Bot,
sidebarLabel: 'AI',
groups,
@@ -495,8 +497,10 @@ export const AISettings = () => {
);
return (
-
- {}} components={panelComponents} />
-
+
+
+ {}} components={panelComponents} />
+
+
);
};
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/IntegrationsSettings/index.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/IntegrationsSettings/index.tsx
index b40d6521..06925b68 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/IntegrationsSettings/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/IntegrationsSettings/index.tsx
@@ -1,12 +1,12 @@
import { useMemo } from 'react';
+import { useNavigate, useParams } from 'react-router';
import { Puzzle, KeyRound, UserCircle, Globe, Wrench, Mail, CalendarDays } from 'lucide-react';
import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceLayout } from 'officerdev';
import { useAuth } from 'hooks/useAuth';
-import { useGlobal } from 'hooks/useGlobal';
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
-import { SettingsSidebar, SettingsContent, type SettingsSection } from '../SettingsPanel';
+import { SettingsRoute, SettingsSidebar, SettingsContent, type SettingsSection } from '../SettingsPanel';
import { GoogleOAuthConfig } from './GoogleOAuthConfig';
// import { GoogleAccount } from './GoogleAccount'; // hidden — email uses IMAP app password now
import { BrowserRelay } from './BrowserRelay';
@@ -14,8 +14,7 @@ import { ApifyConfig } from './ApifyConfig';
import { EmailAccounts } from './EmailAccounts';
import { DavAppPasswords } from './DavAppPasswords';
-const GLOBAL_KEY = 'INTEGRATIONS_SETTINGS_SELECTED';
-const TAB_KEY = 'INTEGRATIONS_SETTINGS_TAB';
+const BASE_PATH = '/settings/integrations';
const enterpriseSections: SettingsSection[] = [
{
@@ -67,10 +66,27 @@ const personalSections: SettingsSection[] = [
},
];
+// The Enterprise/Personal tab is not a second thing to remember — it is a fact about which section is
+// open, so it is derived from the URL rather than kept in its own global. That also fixes the deep link:
+// with the tab held separately, `/settings/integrations/email-accounts` would have arrived with the tab
+// still on Enterprise and the content pane showing "Select a section".
+const allSections = [...enterpriseSections, ...personalSections];
+const tabForSection = (section: string | undefined) =>
+ personalSections.some((s) => s.key === section) ? 'personal' : 'enterprise';
+
const IntegrationsSidebar = () => {
- const [tab, setTab] = useGlobal(TAB_KEY, 'enterprise');
+ const { section } = useParams<{ section?: string }>();
+ const navigate = useNavigate();
+ const tab = tabForSection(section);
const sections = tab === 'enterprise' ? enterpriseSections : personalSections;
+ // Switching tab is a navigation, so it lands on that tab's first section rather than leaving the
+ // content pane showing a section the sidebar no longer lists.
+ const setTab = (next: string) => {
+ const first = (next === 'enterprise' ? enterpriseSections : personalSections)[0];
+ if (first) navigate(`${BASE_PATH}/${first.key}`);
+ };
+
return (
@@ -91,17 +107,14 @@ const IntegrationsSidebar = () => {
-
+
);
};
-const IntegrationsContent = () => {
- const [tab] = useGlobal(TAB_KEY, 'enterprise');
- const sections = tab === 'enterprise' ? enterpriseSections : personalSections;
-
- return ;
-};
+// Both tabs' sections, because the URL alone says which one is open and the pane no longer has to agree
+// with the sidebar about the tab to find it.
+const IntegrationsContent = () => ;
const layout: LayoutNode = {
type: 'group',
@@ -123,8 +136,10 @@ export const IntegrationsSettings = () => {
);
return (
-
- {}} components={panelComponents} />
-
+
+
+ {}} components={panelComponents} />
+
+
);
};
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/ProfileSettings/index.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/ProfileSettings/index.tsx
index 1036f3f1..0d3b2a7a 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/ProfileSettings/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/ProfileSettings/index.tsx
@@ -3,14 +3,14 @@ import { User, Lock, Globe, LayoutGrid, Volume2 } from 'lucide-react';
import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceLayout } from 'officerdev';
-import { createSettingsPanelComponents, type SettingsSection } from '../SettingsPanel';
+import { SettingsRoute, createSettingsPanelComponents, type SettingsSection } from '../SettingsPanel';
import { UserData } from './UserData';
import { ChangePassword } from './ChangePassword';
import { Languages } from './Languages';
import { DockSettings } from './DockSettings';
import { VoicePreference } from './VoicePreference';
-const GLOBAL_KEY = 'PROFILE_SETTINGS_SELECTED';
+const BASE_PATH = '/settings/profile';
const sections: SettingsSection[] = [
{ key: 'profile', icon: User, title: 'Profile', description: 'Avatar, name, and email', content: },
@@ -45,7 +45,7 @@ const sections: SettingsSection[] = [
];
const { Sidebar, Content } = createSettingsPanelComponents({
- globalKey: GLOBAL_KEY,
+ basePath: BASE_PATH,
sidebarIcon: User,
sidebarLabel: 'Profile',
sections,
@@ -71,8 +71,10 @@ export const ProfileSettings = () => {
);
return (
-
- {}} components={panelComponents} />
-
+
+
+ {}} components={panelComponents} />
+
+
);
};
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/SettingsPanel.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/SettingsPanel.tsx
index 681423a1..2db77287 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/SettingsPanel.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/SettingsPanel.tsx
@@ -1,7 +1,7 @@
import type { ComponentType, ReactNode } from 'react';
import { useState } from 'react';
+import { NavLink, Navigate, useParams } from 'react-router';
import type { LucideIcon } from 'lucide-react';
-import { useGlobal } from 'hooks/useGlobal';
import { Input } from '@/components/ui/input';
export type SettingsSection = {
@@ -19,7 +19,7 @@ export type SettingsSectionGroup = {
};
type SettingsSidebarProps = {
- globalKey: string;
+ basePath: string;
icon: LucideIcon;
label: string;
sections: SettingsSection[];
@@ -27,33 +27,33 @@ type SettingsSidebarProps = {
hideHeader?: boolean;
};
-const SectionButton = ({
- section,
- isActive,
- onClick,
-}: {
- section: SettingsSection;
- isActive: boolean;
- onClick: () => void;
-}) => (
- (
+
+ `flex items-start gap-2.5 py-2 px-3 rounded-lg text-left cursor-pointer transition-colors ${
+ isActive ? 'bg-duck-teal/10 text-duck-dark dark:text-foreground' : 'text-duck-dark/70 dark:text-foreground/70 hover:bg-duck-dark/5 dark:hover:bg-foreground/5 hover:text-duck-dark dark:hover:text-foreground'
+ }`
+ }
>
-
-
-
{section.title}
-
{section.description}
-
-
+ {({ isActive }) => (
+ <>
+
+
+
{section.title}
+
{section.description}
+
+ >
+ )}
+
);
-export const SettingsSidebar = ({ globalKey, icon: Icon, label, sections, groups, hideHeader }: SettingsSidebarProps) => {
- const allSections = groups ? groups.flatMap((g) => g.sections) : sections;
- const [selectedKey, setSelectedKey] = useGlobal(globalKey, allSections[0]?.key ?? null);
+export const SettingsSidebar = ({ basePath, icon: Icon, label, sections, groups, hideHeader }: SettingsSidebarProps) => {
const [search, setSearch] = useState('');
const query = search.toLowerCase();
@@ -87,23 +87,13 @@ export const SettingsSidebar = ({ globalKey, icon: Icon, label, sections, groups
{filtered.map((s) => (
- setSelectedKey(s.key)}
- />
+
))}
);
})
: sections.filter(matchesSearch).map((s) => (
- setSelectedKey(s.key)}
- />
+
))}
@@ -111,12 +101,14 @@ export const SettingsSidebar = ({ globalKey, icon: Icon, label, sections, groups
};
type SettingsContentProps = {
- globalKey: string;
sections: SettingsSection[];
};
-export const SettingsContent = ({ globalKey, sections }: SettingsContentProps) => {
- const [selectedKey] = useGlobal(globalKey, sections[0]?.key ?? null);
+export const SettingsContent = ({ sections }: SettingsContentProps) => {
+ // The sidebar and this pane are two sibling panels that used to agree via a shared `useGlobal` key.
+ // They agree via the URL now, so neither has to be told about the other — and neither can be the one
+ // that seeds the slot, which is what made the old defaulting order matter (see §5.6).
+ const { section: selectedKey } = useParams<{ section?: string }>();
const section = sections.find((s) => s.key === selectedKey);
if (!section) {
@@ -139,19 +131,35 @@ export const SettingsContent = ({ globalKey, sections }: SettingsContentProps) =
);
};
+/**
+ * The one guard each settings screen renders, per the route-pair convention: it canonicalises both the
+ * bare page route and a section that does not exist, so `/settings/ai` and `/settings/ai/nonsense` both
+ * land on the first section instead of on "Select a section".
+ *
+ * Place it after the screen's hooks — it returns early.
+ */
+type SettingsRouteProps = { basePath: string; sections: SettingsSection[]; children: ReactNode };
+
+export const SettingsRoute = ({ basePath, sections, children }: SettingsRouteProps) => {
+ const { section } = useParams<{ section?: string }>();
+ const first = sections[0];
+ if (first && !sections.some((s) => s.key === section)) return ;
+ return <>{children}>;
+};
+
type CreateSettingsPanelParams = {
- globalKey: string;
+ basePath: string;
sidebarIcon: LucideIcon;
sidebarLabel: string;
sections?: SettingsSection[];
groups?: SettingsSectionGroup[];
};
-export const createSettingsPanelComponents = ({ globalKey, sidebarIcon, sidebarLabel, sections = [], groups }: CreateSettingsPanelParams) => {
+export const createSettingsPanelComponents = ({ basePath, sidebarIcon, sidebarLabel, sections = [], groups }: CreateSettingsPanelParams) => {
const allSections = groups ? groups.flatMap((g) => g.sections) : sections;
const Sidebar: ComponentType = () => (
-
+
);
- const Content: ComponentType = () => ;
- return { Sidebar, Content };
+ const Content: ComponentType = () => ;
+ return { Sidebar, Content, allSections };
};
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/SystemSettings.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/SystemSettings.tsx
index 91fe4a01..c7087c13 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/SystemSettings.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/SystemSettings.tsx
@@ -3,7 +3,7 @@ import { Bot, Settings, Mail, Volume2, Mic, ScanText } from 'lucide-react';
import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceLayout } from 'officerdev';
-import { createSettingsPanelComponents, type SettingsSectionGroup } from './SettingsPanel';
+import { SettingsRoute, createSettingsPanelComponents, type SettingsSectionGroup } from './SettingsPanel';
import { SMTPSection } from './ServerSettings/SMTPSection';
import { TTSSection } from './ServerSettings/TTSSection';
import { STTSection } from './ServerSettings/STTSection';
@@ -40,8 +40,10 @@ const groups: SettingsSectionGroup[] = [
},
];
-const { Sidebar, Content } = createSettingsPanelComponents({
- globalKey: 'SYSTEM_SETTINGS_SELECTED',
+const BASE_PATH = '/settings/system';
+
+const { Sidebar, Content, allSections } = createSettingsPanelComponents({
+ basePath: BASE_PATH,
sidebarIcon: Settings,
sidebarLabel: 'System',
groups,
@@ -73,8 +75,10 @@ export const SystemSettings = () => {
);
return (
-
- {}} components={panelComponents} />
-
+
+
+ {}} components={panelComponents} />
+
+
);
};
diff --git a/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/index.tsx b/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/index.tsx
index eb26611b..cfce829e 100644
--- a/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/index.tsx
+++ b/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/index.tsx
@@ -2,7 +2,7 @@ import { useMemo } from 'react';
import { Users, UserCog, ShieldCheck } from 'lucide-react';
import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceLayout } from 'officerdev';
-import { createSettingsPanelComponents, type SettingsSection } from '../SettingsPanel';
+import { SettingsRoute, createSettingsPanelComponents, type SettingsSection } from '../SettingsPanel';
import { UsersSection } from './UsersSection';
import { PermissionsSection } from './PermissionsSection';
@@ -28,8 +28,10 @@ const sections: SettingsSection[] = [
},
];
+const BASE_PATH = '/settings/user-management';
+
const { Sidebar, Content } = createSettingsPanelComponents({
- globalKey: 'USER_MANAGEMENT_SELECTED',
+ basePath: BASE_PATH,
sidebarIcon: UserCog,
sidebarLabel: 'Users',
sections,
@@ -52,8 +54,10 @@ export const UserManagementSettings = () => {
);
return (
-
- {}} components={panelComponents} />
-
+
+
+ {}} components={panelComponents} />
+
+
);
};
diff --git a/src/apps/officer-web/locales/en.json b/src/apps/officer-web/locales/en.json
index e75786da..c6b82535 100644
--- a/src/apps/officer-web/locales/en.json
+++ b/src/apps/officer-web/locales/en.json
@@ -52,7 +52,6 @@
"userMenu": {
"profile": "Profile",
"systemSettings": "System Settings",
- "resources": "Resources",
"signOut": "Sign Out"
}
},
diff --git a/src/apps/officer-web/locales/pt.json b/src/apps/officer-web/locales/pt.json
index 23df6e49..ae68936b 100644
--- a/src/apps/officer-web/locales/pt.json
+++ b/src/apps/officer-web/locales/pt.json
@@ -52,7 +52,6 @@
"userMenu": {
"profile": "Perfil",
"systemSettings": "Definições do Sistema",
- "resources": "Recursos",
"signOut": "Terminar Sessão"
}
},