Files
platform/src/apps/officer-web/Screens/Dashboard/Settings/UserManagement/index.tsx
T
pastilhas c3c62828e3 settings framework and section metadata keyed (487 -> 425)
SettingsSection now carries titleKey/descriptionKey instead of title/description, and
the sidebar and content pane resolve them. The section arrays are module-level consts
holding JSX content, so they cannot call a hook — the data holds keys and the renderers
translate. Sidebar search matches the RESOLVED words, so it works in the language on
screen.

Capital K in titleKey is load-bearing: docs-check-settings.ts greps key:\s*'…' for
route segments, and a lowercase title_key would have fed it twenty bogus paths.
Verified 0 bogus, 26 declared, 20 offered, 0 missing.

Section titles now share one key with the Spotlight rows that name the same section,
per 'one key per meaning'. Two casings had to converge, so Settings shows Spotlight's:
'Change Password' now reads 'Change password'.

Settings sidebar search placeholder was 'Search...'; it is now 'Search settings…',
which is what the skeleton catalog already said and what distinguishes it from the
global Spotlight.
2026-08-23 04:46:07 +00:00

64 lines
2.2 KiB
TypeScript

import { useMemo } from 'react';
import { Users, UserCog, ShieldCheck } from 'lucide-react';
import type { LayoutNode, PanelComponents } from 'officerdev';
import { WorkspaceLayout } from 'officerdev';
import { SettingsRoute, createSettingsPanelComponents, type SettingsSection } from '../SettingsPanel';
import { UsersSection } from './UsersSection';
import { PermissionsSection } from './PermissionsSection';
// Owner-only. Every endpoint behind this screen is gated by ownerGate in users-router.ts, and the
// global backstop already confines a non-owner token to /api/auth + /api/music — so a Member reaching
// this route sees the list fail to load rather than someone else's accounts. The route is not hidden
// from them, because hiding a screen is not access control and pretending otherwise invites someone to
// rely on it.
const sections: SettingsSection[] = [
{
key: 'users',
icon: Users,
titleKey: 'settings.sections.accounts.label',
descriptionKey: 'settings.sections.accounts.description',
content: <UsersSection />,
},
{
key: 'permissions',
icon: ShieldCheck,
titleKey: 'settings.sections.permissions.label',
descriptionKey: 'settings.sections.permissions.description',
content: <PermissionsSection />,
},
];
const BASE_PATH = '/settings/user-management';
const { Sidebar, Content } = createSettingsPanelComponents({
basePath: BASE_PATH,
sidebarIcon: UserCog,
sidebarLabelKey: 'settings.nav.users',
sections,
});
const layout: LayoutNode = {
type: 'group',
id: 'user-management-root',
direction: 'horizontal',
children: [
{ node: { type: 'panel', id: 'user-management-left', appType: null }, size: 20 },
{ node: { type: 'panel', id: 'user-management-right', appType: null }, size: 80 },
],
};
export const UserManagementSettings = () => {
const panelComponents: PanelComponents = useMemo(
() => ({ 'user-management-left': Sidebar, 'user-management-right': Content }),
[],
);
return (
<SettingsRoute basePath={BASE_PATH} sections={sections}>
<div className="h-full w-full pt-2">
<WorkspaceLayout layout={layout} onLayoutChange={() => {}} components={panelComponents} />
</div>
</SettingsRoute>
);
};