fix(c6): settings plugin pages permission-filtered, label-dedup hack removed
This commit is contained in:
@@ -3,6 +3,8 @@ import { NavLink, Outlet } from 'react-router-dom';
|
|||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { usePluginStore } from '@/store/pluginStore';
|
import { usePluginStore } from '@/store/pluginStore';
|
||||||
import { useUIStore } from '@/store/uiStore';
|
import { useUIStore } from '@/store/uiStore';
|
||||||
|
import { usePermission } from '@/hooks/usePermission';
|
||||||
|
import { useAuthStore } from '@/store/authStore';
|
||||||
import { Settings, Mail, Bell, Sparkles, Bot, Shield, Users, UsersRound, Package, ArrowLeft } from 'lucide-react';
|
import { Settings, Mail, Bell, Sparkles, Bot, Shield, Users, UsersRound, Package, ArrowLeft } from 'lucide-react';
|
||||||
|
|
||||||
const ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
|
const ICON_MAP: Record<string, React.ComponentType<{ className?: string }>> = {
|
||||||
@@ -22,12 +24,17 @@ export function SettingsPage() {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const manifests = usePluginStore(s => s.manifests);
|
const manifests = usePluginStore(s => s.manifests);
|
||||||
const sidebarOpen = useUIStore((state) => state.sidebarOpen);
|
const sidebarOpen = useUIStore((state) => state.sidebarOpen);
|
||||||
|
const { hasPermission } = usePermission();
|
||||||
|
const user = useAuthStore((state) => state.user);
|
||||||
const pluginSettingsPages = useMemo(
|
const pluginSettingsPages = useMemo(
|
||||||
() => (manifests || [])
|
() => (manifests || [])
|
||||||
.flatMap((m) => (Array.isArray(m.settings_pages) ? m.settings_pages : []))
|
.flatMap((m) => (Array.isArray(m.settings_pages) ? m.settings_pages : []))
|
||||||
.filter((p): p is NonNullable<typeof p> => !!p && !!p.path)
|
.filter((p): p is NonNullable<typeof p> => !!p && !!p.path)
|
||||||
|
// ARCH-006 parity: hide settings pages the user has no permission for
|
||||||
|
// (fail-closed while permissions are loading)
|
||||||
|
.filter(p => !p.permission || (user?.is_system_admin || hasPermission(p.permission)))
|
||||||
.sort((a, b) => a.order - b.order),
|
.sort((a, b) => a.order - b.order),
|
||||||
[manifests]
|
[manifests, user]
|
||||||
);
|
);
|
||||||
|
|
||||||
const hardcodedNavItems = [
|
const hardcodedNavItems = [
|
||||||
@@ -44,11 +51,10 @@ export function SettingsPage() {
|
|||||||
];
|
];
|
||||||
|
|
||||||
const existingPaths = new Set(hardcodedNavItems.map(item => item.to));
|
const existingPaths = new Set(hardcodedNavItems.map(item => item.to));
|
||||||
// Filter out plugin nav items that are duplicates of hardcoded ones
|
// Path-based dedup only — the old label-based hack hid legitimate plugin
|
||||||
const duplicateLabels = new Set(['Roles', 'Users', 'Groups', 'AI Settings', 'Automation', 'Proactive AI']);
|
// pages whenever their English label collided with a hardcoded one.
|
||||||
const pluginNavItems = pluginSettingsPages
|
const pluginNavItems = pluginSettingsPages
|
||||||
.filter(p => !existingPaths.has(`/settings/${p.path}`))
|
.filter(p => !existingPaths.has(`/settings/${p.path}`))
|
||||||
.filter(p => !duplicateLabels.has(p.label))
|
|
||||||
.map(p => ({
|
.map(p => ({
|
||||||
to: `/settings/${p.path}`,
|
to: `/settings/${p.path}`,
|
||||||
label: t(p.label_key, p.label),
|
label: t(p.label_key, p.label),
|
||||||
|
|||||||
Reference in New Issue
Block a user