mirror of
https://github.com/iflytek/skillhub.git
synced 2026-09-24 00:55:35 +00:00
feat(web): reorganize marketplace and console navigation
Made-with: Proma Signed-off-by: XiaoSeS <87064762+XiaoSeS@users.noreply.github.com>
This commit is contained in:
parent
687097ad91
commit
4ef0f5c8ed
12 changed files with 53 additions and 6 deletions
|
|
@ -46,6 +46,8 @@ services:
|
|||
SKILLHUB_SECURITY_SCANNER_ENABLED: "true"
|
||||
SKILLHUB_SECURITY_SCANNER_URL: http://skill-scanner:8000
|
||||
SKILLHUB_SECURITY_SCANNER_MODE: upload
|
||||
# Local staging needs typed Suite review writes so public Suite market fixtures can be submitted and approved.
|
||||
SKILLHUB_SUITE_REVIEW_WRITES_ENABLED: "true"
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@
|
|||
|------|------|------|
|
||||
| 首页 | `/` | 精选/热门/最新、搜索入口 |
|
||||
| 搜索页 | `/search` | 关键词搜索 + 过滤 + 排序 |
|
||||
| 技能套件市场 | `/suites` | 浏览和搜索已发布的技能套件 |
|
||||
| 技能套件详情 | `/suite/{namespace}/{slug}` | 套件说明、成员技能和安装计划 |
|
||||
| 命名空间主页 | `/@{namespace}` | 空间介绍 + 技能列表 |
|
||||
| 技能详情页 | `/@{namespace}/{slug}` | README 渲染、版本、评分、收藏、下载 |
|
||||
| 版本历史 | `/@{namespace}/{slug}/versions` | 版本列表 + changelog |
|
||||
|
|
@ -39,6 +41,7 @@
|
|||
| 页面 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| 我的技能 | `/dashboard/skills` | 我发布的技能 + 统一生命周期状态 |
|
||||
| 我的技能套件 | `/dashboard/suites` | 创建和管理我维护的技能套件 |
|
||||
| 发布技能 | `/dashboard/publish` | zip 上传 + 预览 + 提交审核 |
|
||||
| 我的收藏 | `/dashboard/stars` | 收藏列表 |
|
||||
| Token 管理 | `/dashboard/tokens` | 创建/查看/吊销 |
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ test.describe('Dashboard Shell (Real API)', () => {
|
|||
const sidebar = page.getByRole('complementary')
|
||||
await expect(sidebar.getByRole('link', { name: 'Profile', exact: true })).toBeVisible()
|
||||
await expect(sidebar.getByRole('link', { name: 'My Skills', exact: true })).toBeVisible()
|
||||
await expect(sidebar.getByRole('link', { name: 'My Suites', exact: true })).toBeVisible()
|
||||
await expect(sidebar.getByRole('link', { name: 'API Tokens', exact: true })).toBeVisible()
|
||||
await expect(page.getByText('View and manage all your published skills')).toBeVisible()
|
||||
await expect(page.getByText('View and manage your skill suites')).toBeVisible()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ test.describe('Light and dark theme', () => {
|
|||
window as Window & { __themeAtFirstReactContent?: boolean }
|
||||
).__themeAtFirstReactContent)).toBe(true)
|
||||
|
||||
await page.getByRole('link', { name: 'Search', exact: true }).first().click()
|
||||
await page.getByRole('link', { name: 'Skill Marketplace', exact: true }).first().click()
|
||||
await expect(page).toHaveURL(/\/search(?:\?|$)/)
|
||||
await expect(page.locator('html')).toHaveClass(/dark/)
|
||||
await expect(page.getByPlaceholder('Search skills...')).toBeVisible()
|
||||
|
|
|
|||
|
|
@ -84,11 +84,9 @@ export function Layout() {
|
|||
}> = [
|
||||
{ label: t('nav.landing'), to: '/', exact: true },
|
||||
{ label: t('nav.publish'), to: '/dashboard/publish', auth: true },
|
||||
{ label: t('nav.search'), to: '/search' },
|
||||
{ label: t('nav.suites', { defaultValue: '技能套件' }), to: '/suites' },
|
||||
{ label: t('nav.marketplace'), to: '/search' },
|
||||
{ label: t('nav.suites'), to: '/suites' },
|
||||
{ label: t('nav.dashboard'), to: '/dashboard', auth: true },
|
||||
{ label: t('nav.mySkills'), to: '/dashboard/skills', auth: true },
|
||||
{ label: t('nav.mySuites'), to: '/dashboard/suites', auth: true },
|
||||
]
|
||||
|
||||
const isActive = (to: string, exact?: boolean) => {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"landing": "Home",
|
||||
"home": "Skill Center",
|
||||
"search": "Search",
|
||||
"marketplace": "Skill Marketplace",
|
||||
"skillDetail": "Skill Detail",
|
||||
"explore": "Explore Skills",
|
||||
"dashboard": "Dashboard",
|
||||
|
|
@ -471,6 +472,7 @@
|
|||
"notifications": "Notifications",
|
||||
"skillsAndData": "Skills & Data",
|
||||
"mySkills": "My Skills",
|
||||
"mySuites": "My Suites",
|
||||
"publish": "Publish",
|
||||
"namespaces": "Namespaces",
|
||||
"stars": "My Stars",
|
||||
|
|
@ -484,6 +486,8 @@
|
|||
"overview": {
|
||||
"mySkills": "My Skills",
|
||||
"mySkillsDesc": "View and manage all your published skills",
|
||||
"mySuites": "My Suites",
|
||||
"mySuitesDesc": "View and manage your skill suites",
|
||||
"publish": "Publish",
|
||||
"publishDesc": "Upload and publish a new skill",
|
||||
"tokens": "API Tokens",
|
||||
|
|
@ -1559,6 +1563,7 @@
|
|||
"menu": {
|
||||
"dashboard": "Dashboard",
|
||||
"mySkills": "My Skills",
|
||||
"mySuites": "My Suites",
|
||||
"myNamespaces": "My Namespaces",
|
||||
"governance": "Governance Center",
|
||||
"stars": "Starred",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"landing": "Главная",
|
||||
"home": "Центр скиллов",
|
||||
"search": "Поиск",
|
||||
"marketplace": "Каталог навыков",
|
||||
"skillDetail": "Карточка скилла",
|
||||
"explore": "Обзор скиллов",
|
||||
"dashboard": "Панель управления",
|
||||
|
|
@ -471,6 +472,7 @@
|
|||
"notifications": "Уведомления",
|
||||
"skillsAndData": "Скиллы и данные",
|
||||
"mySkills": "Мои скиллы",
|
||||
"mySuites": "Мои наборы",
|
||||
"publish": "Публикация",
|
||||
"namespaces": "Пространства",
|
||||
"stars": "Мои звёзды",
|
||||
|
|
@ -484,6 +486,8 @@
|
|||
"overview": {
|
||||
"mySkills": "Мои скиллы",
|
||||
"mySkillsDesc": "Просмотр и управление опубликованными скиллами",
|
||||
"mySuites": "Мои наборы",
|
||||
"mySuitesDesc": "Просмотр и управление наборами навыков",
|
||||
"publish": "Публикация",
|
||||
"publishDesc": "Загрузить и опубликовать новый скилл",
|
||||
"tokens": "Токены API",
|
||||
|
|
@ -1878,6 +1882,7 @@
|
|||
"menu": {
|
||||
"dashboard": "Панель",
|
||||
"mySkills": "Мои скиллы",
|
||||
"mySuites": "Мои наборы",
|
||||
"myNamespaces": "Мои пространства имён",
|
||||
"namespacesAdmin": "Управление пространствами имён",
|
||||
"governance": "Центр управления",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"landing": "首页",
|
||||
"home": "技能中心",
|
||||
"search": "搜索",
|
||||
"marketplace": "技能市场",
|
||||
"skillDetail": "技能详情",
|
||||
"explore": "探索",
|
||||
"dashboard": "控制台",
|
||||
|
|
@ -471,6 +472,7 @@
|
|||
"notifications": "通知设置",
|
||||
"skillsAndData": "技能与数据",
|
||||
"mySkills": "我的技能",
|
||||
"mySuites": "我的套件",
|
||||
"publish": "技能发布",
|
||||
"namespaces": "命名空间",
|
||||
"stars": "我的收藏",
|
||||
|
|
@ -484,6 +486,8 @@
|
|||
"overview": {
|
||||
"mySkills": "我的技能",
|
||||
"mySkillsDesc": "查看和管理你发布的所有技能",
|
||||
"mySuites": "我的套件",
|
||||
"mySuitesDesc": "查看和管理你的技能套件",
|
||||
"publish": "技能发布",
|
||||
"publishDesc": "上传并发布新的技能到平台",
|
||||
"tokens": "Token 凭证",
|
||||
|
|
@ -1558,6 +1562,7 @@
|
|||
"menu": {
|
||||
"dashboard": "控制台",
|
||||
"mySkills": "我的技能",
|
||||
"mySuites": "我的套件",
|
||||
"myNamespaces": "我的命名空间",
|
||||
"governance": "治理中心",
|
||||
"stars": "我的收藏",
|
||||
|
|
|
|||
|
|
@ -67,6 +67,8 @@ describe('DashboardPage', () => {
|
|||
|
||||
expect(html).toContain('sidebar.skillsAndData')
|
||||
expect(html).toContain('overview.mySkills')
|
||||
expect(html).toContain('sidebar.mySuites')
|
||||
expect(html).toContain('overview.mySuites')
|
||||
expect(html).not.toContain('overview.publish')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { canViewGovernanceCenter } from '@/shared/lib/governance-access'
|
|||
import { APP_SHELL_PAGE_CLASS_NAME } from '@/app/page-shell-style'
|
||||
import { DashboardPageHeader } from '@/shared/components/dashboard-page-header'
|
||||
import {
|
||||
Star, Heart, Package, Key, Shield, Flag, Globe,
|
||||
Star, Heart, Package, Boxes, Key, Shield, Flag, Globe,
|
||||
UserCog, Lock, Bell, Clock, ChevronRight,
|
||||
} from 'lucide-react'
|
||||
|
||||
|
|
@ -44,6 +44,7 @@ export const SIDEBAR_GROUPS: SidebarGroup[] = [
|
|||
label: 'sidebar.skillsAndData',
|
||||
items: [
|
||||
{ key: 'skills', icon: Package, label: 'sidebar.mySkills', to: '/dashboard/skills' },
|
||||
{ key: 'suites', icon: Boxes, label: 'sidebar.mySuites', to: '/dashboard/suites' },
|
||||
{ key: 'namespaces', icon: Globe, label: 'sidebar.namespaces', to: '/dashboard/namespaces' },
|
||||
{ key: 'stars', icon: Star, label: 'sidebar.stars', to: '/dashboard/stars' },
|
||||
{ key: 'subscriptions', icon: Heart, label: 'sidebar.subscriptions', to: '/dashboard/subscriptions' },
|
||||
|
|
@ -79,6 +80,7 @@ export const SIDEBAR_NAV = SIDEBAR_NAV_ITEMS.map(({ key, icon, label, to, admin,
|
|||
*/
|
||||
const OVERVIEW_CARDS = [
|
||||
{ key: 'skills', icon: Package, label: 'overview.mySkills', to: '/dashboard/skills', desc: 'overview.mySkillsDesc' },
|
||||
{ key: 'suites', icon: Boxes, label: 'overview.mySuites', to: '/dashboard/suites', desc: 'overview.mySuitesDesc' },
|
||||
{ key: 'tokens', icon: Key, label: 'overview.tokens', to: '/dashboard/tokens', desc: 'overview.tokensDesc' },
|
||||
{ key: 'stars', icon: Star, label: 'overview.stars', to: '/dashboard/stars', desc: 'overview.starsDesc' },
|
||||
{ key: 'profile', icon: UserCog, label: 'overview.profile', to: '/settings/profile', desc: 'overview.profileDesc' },
|
||||
|
|
|
|||
|
|
@ -77,6 +77,23 @@ describe('user-menu module exports', () => {
|
|||
})
|
||||
|
||||
describe('UserMenu navigation', () => {
|
||||
it('lists personal destinations in the requested order', () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<UserMenu
|
||||
user={{
|
||||
displayName: 'Skill Author',
|
||||
platformRoles: ['USER'],
|
||||
}}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(html).toContain('user.menu.mySkills')
|
||||
expect(html).toContain('user.menu.mySuites')
|
||||
expect(html).toContain('user.menu.dashboard')
|
||||
expect(html.indexOf('user.menu.mySkills')).toBeLessThan(html.indexOf('user.menu.mySuites'))
|
||||
expect(html.indexOf('user.menu.mySuites')).toBeLessThan(html.indexOf('user.menu.dashboard'))
|
||||
})
|
||||
|
||||
it('keeps dashboard-only personal links out of the compact avatar menu', () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<UserMenu
|
||||
|
|
|
|||
|
|
@ -142,6 +142,12 @@ export function UserMenu({ user, triggerClassName }: UserMenuProps) {
|
|||
role="menu"
|
||||
className="overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-md"
|
||||
>
|
||||
<Link to="/dashboard/skills" className={menuItemClassName} onClick={closeMenu}>
|
||||
{t('user.menu.mySkills')}
|
||||
</Link>
|
||||
<Link to="/dashboard/suites" className={menuItemClassName} onClick={closeMenu}>
|
||||
{t('user.menu.mySuites')}
|
||||
</Link>
|
||||
<Link to="/dashboard" className={menuItemClassName} onClick={closeMenu}>
|
||||
{t('user.menu.dashboard')}
|
||||
</Link>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue