feat: hide skill_entry when use has no skill.view permission (#41429)

This commit is contained in:
wangxiaolei
2026-08-28 05:13:38 +00:00
committed by GitHub
parent 97b5d8092c
commit ff5d431efd
5 changed files with 27 additions and 12 deletions
+1 -11
View File
@@ -1068,11 +1068,6 @@
"count": 2
}
},
"web/app/components/base/icons/src/public/billing/index.ts": {
"no-barrel-files/no-barrel-files": {
"count": 4
}
},
"web/app/components/base/icons/src/public/common/index.ts": {
"no-barrel-files/no-barrel-files": {
"count": 5
@@ -1767,11 +1762,6 @@
"count": 3
}
},
"web/app/components/billing/pricing/assets/index.tsx": {
"no-barrel-files/no-barrel-files": {
"count": 12
}
},
"web/app/components/datasets/chunk.tsx": {
"jsx-a11y/label-has-associated-control": {
"count": 2
@@ -5408,4 +5398,4 @@
"count": 2
}
}
}
}
@@ -465,6 +465,7 @@ const ownerWorkspacePermissionKeys = [
'tool.manage',
'mcp.manage',
'agent.manage',
'skill.view',
]
const datasetOperatorWorkspacePermissionKeys = [
@@ -784,6 +785,17 @@ describe('MainNav', () => {
expect(screen.queryByRole('link', { name: /common.mainNav.skills/ })).not.toBeInTheDocument()
})
it('hides the skills entry when the user lacks skill.view', () => {
mockConsoleState.current = {
...consoleState,
workspacePermissionKeys: ownerWorkspacePermissionKeys.filter((key) => key !== 'skill.view'),
}
renderMainNav()
expect(screen.queryByRole('link', { name: /common.mainNav.skills/ })).not.toBeInTheDocument()
})
it('orders the Step-by-step Tour before the account and help actions', async () => {
localStorage.setItem(STEP_BY_STEP_TOUR_SHELL_MODE_STORAGE_KEY, 'collapsed')
+4
View File
@@ -15,6 +15,7 @@ import { isCurrentWorkspaceDatasetOperatorAtom } from '@/context/workspace-state
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { isAgentV2Enabled } from '@/features/agent-v2/feature-flag'
import { useCanManageAgents } from '@/features/agent-v2/permissions'
import { useCanViewSkills } from '@/features/skills/permissions'
import { systemFeaturesQueryOptions } from '@/features/system-features/client'
import dynamic from '@/next/dynamic'
import Link from '@/next/link'
@@ -39,6 +40,7 @@ export function MainNav({ className }: MainNavProps) {
})
const agentV2Enabled = isAgentV2Enabled()
const canManageAgents = useCanManageAgents()
const canViewSkills = useCanViewSkills()
const enableSkill = useProviderContextSelector((state) => state.enableSkill)
const showEnvTag = currentEnv === 'TESTING' || currentEnv === 'DEVELOPMENT'
const helpMenuTriggerRef = useRef<HTMLButtonElement>(null)
@@ -49,6 +51,7 @@ export function MainNav({ className }: MainNavProps) {
isMainNavRouteVisible(route, {
agentV2Enabled,
canManageAgents,
canViewSkills,
isCurrentWorkspaceDatasetOperator,
marketplaceEnabled: systemFeatures.enable_marketplace,
skillEnabled: enableSkill,
@@ -63,6 +66,7 @@ export function MainNav({ className }: MainNavProps) {
[
agentV2Enabled,
canManageAgents,
canViewSkills,
enableSkill,
isCurrentWorkspaceDatasetOperator,
systemFeatures.enable_marketplace,
+2 -1
View File
@@ -18,6 +18,7 @@ export type MainNavRouteConfig = {
export type MainNavRouteVisibilityOptions = {
agentV2Enabled: boolean
canManageAgents: boolean
canViewSkills: boolean
isCurrentWorkspaceDatasetOperator: boolean
marketplaceEnabled: boolean
skillEnabled: boolean
@@ -31,7 +32,7 @@ export type DetailSidebarVisibilityOptions = Pick<
const VISIBLE_TO_ALL: MainNavRouteVisibility = () => true
const CAN_MANAGE_AGENTS: MainNavRouteVisibility = (options) => options.canManageAgents
const SKILL_ENABLED_FOR_WORKSPACE: MainNavRouteVisibility = (options) =>
options.skillEnabled && !options.isCurrentWorkspaceDatasetOperator
options.skillEnabled && options.canViewSkills && !options.isCurrentWorkspaceDatasetOperator
function isPathUnderRoute(pathname: string, route: string) {
return pathname === route || pathname.startsWith(`${route}/`)
+8
View File
@@ -3,15 +3,23 @@ import { workspacePermissionKeysAtom } from '@/context/permission-state'
import { hasPermission } from '@/utils/permission'
const SkillPermission = {
View: 'skill.view',
Edit: 'skill.edit',
Publish: 'skill.publish',
Delete: 'skill.delete',
} as const
export function useCanViewSkills() {
const permissionKeys = useAtomValue(workspacePermissionKeysAtom)
return hasPermission(permissionKeys, SkillPermission.View)
}
export function useSkillPermissions() {
const permissionKeys = useAtomValue(workspacePermissionKeysAtom)
return {
canView: hasPermission(permissionKeys, SkillPermission.View),
canEdit: hasPermission(permissionKeys, SkillPermission.Edit),
canPublish: hasPermission(permissionKeys, SkillPermission.Publish),
canDelete: hasPermission(permissionKeys, SkillPermission.Delete),