Skip to content

feat: System Settings Hub, App Management, and Permission Management pages#767

Merged
hotlong merged 3 commits intomainfrom
copilot/add-app-management-page
Feb 23, 2026
Merged

feat: System Settings Hub, App Management, and Permission Management pages#767
hotlong merged 3 commits intomainfrom
copilot/add-app-management-page

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Feb 23, 2026

Console lacked a unified system settings entry point, an app management page, and a permission management page. Settings only pointed to profile; app management was limited to sidebar dropdown actions.

New pages

  • SystemHubPage (/system/) — Card grid linking to all admin sections (Apps, Users, Orgs, Roles, Permissions, Audit Log, Profile) with live record counts
  • AppManagementPage (/system/apps) — Full app list with search, enable/disable toggle, set-default, delete w/ confirmation, bulk select operations, and links to create/edit flows
  • PermissionManagementPage (/system/permissions) — CRUD table for sys_permission with search filter and admin-gated create/delete

Navigation changes

  • Sidebar Settings → now navigates to /system/ hub (was /system/profile)
  • App switcher dropdown → added "Manage All Apps" entry linking to /system/apps

Routes added in App.tsx

<Route path="system" element={<SystemHubPage />} />
<Route path="system/apps" element={<AppManagementPage />} />
<Route path="system/permissions" element={<PermissionManagementPage />} />

Tests

  • 11 new tests covering hub card rendering/navigation, app list filtering, permission CRUD and search — 20 total system page tests passing

Notes

  • App management actions (toggle, delete, set-default) show toast feedback but are stubbed with TODO comments pending backend API support
  • Hub counts use dataSource.find() with a TODO for count-specific endpoints
  • ROADMAP updated with P1.12 section
Original prompt

This section details on the original issue you should resolve

<issue_title>系统设置与应用管理中心:新增应用清单与集中入口</issue_title>
<issue_description>## 背景
现有 Console 已支持用户、组织、角色、审计日志、个人资料等系统设置页面(/system/users、/organizations、/roles、/audit-log、/profile),但缺失"应用清单/应用管理"页面,以及真正的 System Hub 统一入口。

当前现况:

  • 配置和切换 App 只能通过 Sidebar 下拉,缺乏完整的全局列表、搜索、启用/禁用与删除入口。
  • Settings 仅指向个人资料页,无法作为设置中心。
  • systemObjects 已定义 sys_permission,但也缺一个权限管理页。

目标

  1. 新增"应用清单/管理"页面(AppManagementPage):集中列出��有已配置 App,支持:

    • 查询/筛选/分页所有 App(名称/描述/状态/启用/默认)
    • 新建(跳转 CreateAppPage)、编辑(跳转 EditAppPage)、删除 App
    • 启用/停用 App(toggle active 字段)
    • 设置默认 App
    • 支持批量操作(启用/禁用/删除)和二次确认
    • 支持导航到应用主页、以及应用设置(如导航设计、品牌等二级入口)
  2. 新增 System Settings 总览页(SystemHubPage,路径 /system/ 下直接入口):

    • 以卡片/列表形式横向(或网格)展示各类管理功能,包括:App清单、用户、组织、角色、权限、审计日志、个人资料
    • 提供所有设置分支的跳转,可视化各分支统计总量(如用户数/应用数/组织数等)
    • 统一 Settings/系统设置菜单,所有跳转由该页分发
  3. 权限管理页补全(PermissionManagementPage):

    • 以 grid/table 形式展示 sys_permission 对象,增删改查
    • 支持搜索、批量授权、角色分配
  4. Sidebar 和 Setting 入口优化:

    • Sidebar 用户区点击 Settings 按钮跳转至 /system/ 统一 Hub
    • System Hub 提供子页 Tab 或卡片(App/用户/组织/权限/...)
    • Sidebar/AppSwitcher 下拉扩展"管理所有应用"直达 AppManagementPage
  5. 路由调整与文档同步:

    • /system/ 默认进入 System Hub,保留 /system/profile 作为子页
    • 文档、Console 结构图更新,清晰标注各管理入口与二级页面链路

参考细节

  • 权限控制建议使用 usePermissions hook 控制入口展示
  • 关键页面/表格建议支持批量操作和分页
  • 组件可复用现有 Table、Card、Modal 等统一风格
  • 若数据量较大,后端接口需支持分页、检索、排序

投交标准

  • 能在单处系统中心页发现所有管理分支和功能
  • 任一 App 的增删改查、启用/禁用均可视、可控
  • 所有系统管理功能 card/link 在统一入口可见,并有清晰统计
  • 权限管理支持角色授权、分配
  • 所有改动有测试及文档/路由结构更新</issue_description>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

@vercel
Copy link
Copy Markdown

vercel bot commented Feb 23, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
objectui Ready Ready Preview, Comment Feb 23, 2026 6:46am
objectui-demo Error Error Feb 23, 2026 6:46am
objectui-storybook Error Error Feb 23, 2026 6:46am

Request Review

…with routes and sidebar updates

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
… for API integration

Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
Copilot AI changed the title [WIP] Add application management page for system settings feat: System Settings Hub, App Management, and Permission Management pages Feb 23, 2026
Copilot AI requested a review from hotlong February 23, 2026 06:34
@hotlong hotlong marked this pull request as ready for review February 23, 2026 08:12
Copilot AI review requested due to automatic review settings February 23, 2026 08:12
@hotlong hotlong merged commit 002f6ee into main Feb 23, 2026
4 of 6 checks passed
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a unified system administration center for the Console app, adding three new pages (SystemHubPage, AppManagementPage, PermissionManagementPage) and updating navigation to provide a centralized entry point for all system settings. This addresses the issue that Console lacked a proper admin hub and app management capabilities beyond the sidebar dropdown.

Changes:

  • Added SystemHubPage at /system/ as the unified entry point with card-based navigation to all admin sections (Apps, Users, Orgs, Roles, Permissions, Audit Log, Profile) with live record counts
  • Added AppManagementPage at /system/apps providing a comprehensive app list with search, enable/disable toggle, set-default, delete with confirmation, and bulk operations
  • Added PermissionManagementPage at /system/permissions for CRUD operations on sys_permission records with search and admin-only controls
  • Updated AppSidebar Settings navigation from /system/profile to /system/ and added "Manage All Apps" entry in app switcher dropdown
  • Added comprehensive test coverage with 11 new tests covering all three pages

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
apps/console/src/pages/system/SystemHubPage.tsx New unified system settings hub page with card grid linking to all admin sections and displaying live statistics
apps/console/src/pages/system/AppManagementPage.tsx New comprehensive app management page with search, filtering, CRUD operations, and bulk actions (operations stubbed pending backend API)
apps/console/src/pages/system/PermissionManagementPage.tsx New permission management page for sys_permission CRUD with search and admin-gated controls
apps/console/src/components/AppSidebar.tsx Updated Settings button to navigate to /system/ hub instead of /system/profile, added "Manage All Apps" entry in app switcher dropdown
apps/console/src/App.tsx Added lazy-loaded routes for SystemHubPage, AppManagementPage, and PermissionManagementPage
apps/console/src/tests/SystemPages.test.tsx Added 11 comprehensive tests covering hub navigation, app filtering, permission CRUD, and search functionality
ROADMAP.md Updated to reflect completion of P1.12 System Settings & App Management Center with detailed checklist

Comment on lines +4 to +6
* Unified entry point for all system administration functions.
* Displays card-based overview linking to Apps, Users, Organizations,
* Roles, Permissions, Audit Log, and Profile management pages.
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation is missing for the new system pages. According to Rule #2 of the coding guidelines, every feature must have corresponding documentation updates. The new SystemHubPage, AppManagementPage, and PermissionManagementPage should be documented in content/docs/guide/console-architecture.md or content/docs/guide/console.md to reflect the new routing structure and system administration capabilities.

Suggested change
* Unified entry point for all system administration functions.
* Displays card-based overview linking to Apps, Users, Organizations,
* Roles, Permissions, Audit Log, and Profile management pages.
* Unified entry point for all system administration functions within the Console.
*
* Routing
* -------
* This page is mounted under the application-scoped system namespace:
* /apps/:appName/system
*
* Each card in the grid links to a specific system administration sub-route:
* - /apps/:appName/system/apps Application catalog and management
* - /apps/:appName/system/users User directory and account management
* - /apps/:appName/system/organizations Organization and team hierarchy
* - /apps/:appName/system/roles Role definitions and access levels
* - /apps/:appName/system/permissions Permission rules and assignments
* - /apps/:appName/system/audit-log System activity and change history
* - /apps/:appName/system/profile Current user profile and settings
*
* Documentation
* --------------
* The System Hub and its sub-pages are described in:
* - content/docs/guide/console-architecture.md
* Documents how system routes are composed under /apps/:appName/system
* and how this hub fits into the overall Console layout.
* - content/docs/guide/console.md
* Provides an end-user overview of system administration capabilities,
* including Applications, Users, Organizations, Roles, Permissions,
* Audit Log, and Profile management flows.

Copilot uses AI. Check for mistakes.
Comment on lines +120 to +159
<div className="overflow-x-auto">
<table className="w-full text-sm" data-testid="permissions-table">
<thead>
<tr className="border-b text-left">
{columns.map((col: string) => (
<th key={col} className="py-2 px-3 font-medium text-muted-foreground capitalize">
{permObject.fields.find((f: any) => f.name === col)?.label || col}
</th>
))}
{isAdmin && <th className="py-2 px-3 font-medium text-muted-foreground">Actions</th>}
</tr>
</thead>
<tbody>
{filteredRecords.map((rec: any) => (
<tr key={rec.id} className="border-b hover:bg-muted/50">
{columns.map((col: string) => (
<td key={col} className="py-2 px-3">
{col === 'action' ? (
<Badge variant="outline">{rec[col]}</Badge>
) : (
<span className="truncate">{rec[col] ?? '—'}</span>
)}
</td>
))}
{isAdmin && (
<td className="py-2 px-3">
<Button
variant="ghost"
size="icon"
onClick={() => handleDelete(rec.id)}
>
<Trash2 className="h-4 w-4" />
</Button>
</td>
)}
</tr>
))}
</tbody>
</table>
</div>
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The layout structure of PermissionManagementPage differs from existing system pages. Compare with UserManagementPage, RoleManagementPage, and AuditLogPage which wrap their tables in Card and CardContent components. This page renders the table directly without a Card wrapper (lines 121-159), creating visual inconsistency. Consider wrapping the table in a Card component for consistency.

Suggested change
<div className="overflow-x-auto">
<table className="w-full text-sm" data-testid="permissions-table">
<thead>
<tr className="border-b text-left">
{columns.map((col: string) => (
<th key={col} className="py-2 px-3 font-medium text-muted-foreground capitalize">
{permObject.fields.find((f: any) => f.name === col)?.label || col}
</th>
))}
{isAdmin && <th className="py-2 px-3 font-medium text-muted-foreground">Actions</th>}
</tr>
</thead>
<tbody>
{filteredRecords.map((rec: any) => (
<tr key={rec.id} className="border-b hover:bg-muted/50">
{columns.map((col: string) => (
<td key={col} className="py-2 px-3">
{col === 'action' ? (
<Badge variant="outline">{rec[col]}</Badge>
) : (
<span className="truncate">{rec[col] ?? '—'}</span>
)}
</td>
))}
{isAdmin && (
<td className="py-2 px-3">
<Button
variant="ghost"
size="icon"
onClick={() => handleDelete(rec.id)}
>
<Trash2 className="h-4 w-4" />
</Button>
</td>
)}
</tr>
))}
</tbody>
</table>
</div>
<Card>
<CardContent className="p-0">
<div className="overflow-x-auto">
<table className="w-full text-sm" data-testid="permissions-table">
<thead>
<tr className="border-b text-left">
{columns.map((col: string) => (
<th key={col} className="py-2 px-3 font-medium text-muted-foreground capitalize">
{permObject.fields.find((f: any) => f.name === col)?.label || col}
</th>
))}
{isAdmin && <th className="py-2 px-3 font-medium text-muted-foreground">Actions</th>}
</tr>
</thead>
<tbody>
{filteredRecords.map((rec: any) => (
<tr key={rec.id} className="border-b hover:bg-muted/50">
{columns.map((col: string) => (
<td key={col} className="py-2 px-3">
{col === 'action' ? (
<Badge variant="outline">{rec[col]}</Badge>
) : (
<span className="truncate">{rec[col] ?? '—'}</span>
)}
</td>
))}
{isAdmin && (
<td className="py-2 px-3">
<Button
variant="ghost"
size="icon"
onClick={() => handleDelete(rec.id)}
>
<Trash2 className="h-4 w-4" />
</Button>
</td>
)}
</tr>
))}
</tbody>
</table>
</div>
</CardContent>
</Card>

Copilot uses AI. Check for mistakes.
Comment on lines +84 to +91
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3">
<div className="min-w-0 flex items-center gap-2">
<Key className="h-5 w-5 text-muted-foreground" />
<div>
<h1 className="text-xl sm:text-2xl font-bold tracking-tight">Permissions</h1>
<p className="text-sm text-muted-foreground">Manage permission rules and assignments</p>
</div>
</div>
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header layout structure differs from the established pattern. Other system pages (UserManagementPage, RoleManagementPage, AuditLogPage) wrap the icon and text in nested divs with specific classes: outer div with "flex items-center gap-3 min-w-0", icon wrapper with "bg-primary/10 p-2 rounded-md shrink-0". This page uses a simpler structure with "min-w-0 flex items-center gap-2" and no background wrapper for the icon. Consider aligning with the established pattern for visual consistency.

Copilot uses AI. Check for mistakes.
Comment on lines +72 to +128
setProcessing(true);
try {
const newActive = app.active === false;
// TODO: Replace with real API call when backend supports app management
toast.success(`${app.label || app.name} ${newActive ? 'enabled' : 'disabled'}`);
await refresh();
} catch {
toast.error('Failed to toggle app status');
} finally {
setProcessing(false);
}
}, [refresh]);

const handleSetDefault = useCallback(async (app: any) => {
setProcessing(true);
try {
// TODO: Replace with real API call when backend supports app management
toast.success(`${app.label || app.name} set as default`);
await refresh();
} catch {
toast.error('Failed to set default app');
} finally {
setProcessing(false);
}
}, [refresh]);

const handleDelete = useCallback(async (appToDelete: any) => {
if (confirmDelete !== appToDelete.name) {
setConfirmDelete(appToDelete.name);
return;
}
setProcessing(true);
try {
// TODO: Replace with real API call when backend supports app management
toast.success(`${appToDelete.label || appToDelete.name} deleted`);
setConfirmDelete(null);
await refresh();
} catch {
toast.error('Failed to delete app');
} finally {
setProcessing(false);
}
}, [confirmDelete, refresh]);

const handleBulkToggle = useCallback(async (active: boolean) => {
setProcessing(true);
try {
// TODO: Replace with real API call when backend supports app management
toast.success(`${selectedIds.size} apps ${active ? 'enabled' : 'disabled'}`);
setSelectedIds(new Set());
await refresh();
} catch {
toast.error('Bulk operation failed');
} finally {
setProcessing(false);
}
}, [selectedIds, refresh]);
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The action buttons (toggle, set-default, delete) are functional in the UI but show toast feedback without actually performing backend operations (lines 71-114). While the TODO comments acknowledge this is pending backend support, these non-functional actions could confuse users during testing. Consider either: (1) disabling these buttons with a tooltip explaining they require backend support, or (2) implementing optimistic UI updates with proper state management so the UI reflects the expected behavior even if not persisted.

Suggested change
setProcessing(true);
try {
const newActive = app.active === false;
// TODO: Replace with real API call when backend supports app management
toast.success(`${app.label || app.name} ${newActive ? 'enabled' : 'disabled'}`);
await refresh();
} catch {
toast.error('Failed to toggle app status');
} finally {
setProcessing(false);
}
}, [refresh]);
const handleSetDefault = useCallback(async (app: any) => {
setProcessing(true);
try {
// TODO: Replace with real API call when backend supports app management
toast.success(`${app.label || app.name} set as default`);
await refresh();
} catch {
toast.error('Failed to set default app');
} finally {
setProcessing(false);
}
}, [refresh]);
const handleDelete = useCallback(async (appToDelete: any) => {
if (confirmDelete !== appToDelete.name) {
setConfirmDelete(appToDelete.name);
return;
}
setProcessing(true);
try {
// TODO: Replace with real API call when backend supports app management
toast.success(`${appToDelete.label || appToDelete.name} deleted`);
setConfirmDelete(null);
await refresh();
} catch {
toast.error('Failed to delete app');
} finally {
setProcessing(false);
}
}, [confirmDelete, refresh]);
const handleBulkToggle = useCallback(async (active: boolean) => {
setProcessing(true);
try {
// TODO: Replace with real API call when backend supports app management
toast.success(`${selectedIds.size} apps ${active ? 'enabled' : 'disabled'}`);
setSelectedIds(new Set());
await refresh();
} catch {
toast.error('Bulk operation failed');
} finally {
setProcessing(false);
}
}, [selectedIds, refresh]);
// App activation toggling is not yet supported until backend APIs are available.
toast.info(
`Toggling ${app.label || app.name} is not yet supported. Backend API integration is pending.`
);
}, []);
const handleSetDefault = useCallback(async (app: any) => {
// Setting a default app is not yet supported until backend APIs are available.
toast.info(
`Setting ${app.label || app.name} as the default app is not yet supported. Backend API integration is pending.`
);
}, []);
const handleDelete = useCallback(async (appToDelete: any) => {
// Deleting apps is not yet supported until backend APIs are available.
if (confirmDelete !== appToDelete.name) {
setConfirmDelete(appToDelete.name);
return;
}
toast.info(
`Deleting ${appToDelete.label || appToDelete.name} is not yet supported. Backend API integration is pending.`
);
setConfirmDelete(null);
}, [confirmDelete]);
const handleBulkToggle = useCallback(async (active: boolean) => {
// Bulk enable/disable is not yet supported until backend APIs are available.
const actionLabel = active ? 'enable' : 'disable';
toast.info(
`Bulk ${actionLabel} for ${selectedIds.size} app(s) is not yet supported. Backend API integration is pending.`
);
setSelectedIds(new Set());
}, [selectedIds]);

Copilot uses AI. Check for mistakes.
Comment on lines +64 to +68
dataSource.find('sys_user').catch(() => ({ data: [] })),
dataSource.find('sys_org').catch(() => ({ data: [] })),
dataSource.find('sys_role').catch(() => ({ data: [] })),
dataSource.find('sys_permission').catch(() => ({ data: [] })),
dataSource.find('sys_audit_log').catch(() => ({ data: [] })),
Copy link

Copilot AI Feb 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fetchCounts function fetches full datasets using find() and counts the results client-side (lines 63-69). This is inefficient for large datasets and is acknowledged in the TODO comment on line 62. However, this pattern could cause performance issues or timeouts if any system table has thousands of records. Consider either: (1) adding a limit to the find() queries to at least cap the transferred data, or (2) using a more robust placeholder approach like showing a loading indicator without actual counts until the backend endpoint is available.

Suggested change
dataSource.find('sys_user').catch(() => ({ data: [] })),
dataSource.find('sys_org').catch(() => ({ data: [] })),
dataSource.find('sys_role').catch(() => ({ data: [] })),
dataSource.find('sys_permission').catch(() => ({ data: [] })),
dataSource.find('sys_audit_log').catch(() => ({ data: [] })),
dataSource.find('sys_user', { limit: 1000 }).catch(() => ({ data: [] })),
dataSource.find('sys_org', { limit: 1000 }).catch(() => ({ data: [] })),
dataSource.find('sys_role', { limit: 1000 }).catch(() => ({ data: [] })),
dataSource.find('sys_permission', { limit: 1000 }).catch(() => ({ data: [] })),
dataSource.find('sys_audit_log', { limit: 1000 }).catch(() => ({ data: [] })),

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

系统设置与应用管理中心:新增应用清单与集中入口

3 participants