feat(portal-shell): v2.0 P1-P4 token migration + unit tests + prod endpoint + e2e
P1: 31 widget 旧纸感令牌批量迁移到 shadcn 标准(1104 次替换) - bg-paper→bg-background / bg-surface→bg-card / text-ink→text-foreground - 保留 button.tsx 中 bg-accent(shadcn 标准 hover 语义令牌) P2: v2.0 新增组件单元测试补齐(5 文件 81 用例) - permission-bitmap: 24 用例(含 GRADE_READ 重复去重) - route-permissions: 26 用例(4 张表优先级 + AND/OR 语义) - notify: 12 用例(sonner toast 双重性质 vi.hoisted mock) - use-error-report: 9 用例(jsdom Blob vi.stubGlobal mock) - plugin-boundary: 10 用例(错误边界 + 骨架变体) P3: 错误上报端点生产替换(后端 /api/v1/log) - api-gateway: internal/log/handler.go(slog 结构化日志,64KB 限制,204 返回) - main.go: 注册 POST /api/v1/log 路由 - useErrorReport: 环境感知端点(prod→/api/v1/log,dev→/api/log) P4: E2E 测试(3 文件 30 用例) - streaming: 4 用例(React 19 use() + Suspense,act 包裹 render) - error-boundaries: 6 用例(三级错误边界层级 L1/L2/L3) - security-boundaries: 20 用例(L1 角色门禁 + L2 权限点 + L3 数据范围) - vitest setup: IS_REACT_ACT_ENVIRONMENT + jest-dom matchers 验证:typecheck 0 错误 / lint 0 错误 / build 6 路由 / 206 测试全部通过
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* rbac-manager(admin / main)
|
||||
@@ -79,20 +79,20 @@ export default function RbacManager(_props: PluginProps): React.ReactElement {
|
||||
|
||||
if (roles.length === 0) {
|
||||
return (
|
||||
<section className="rounded-card border border-rule bg-surface p-md">
|
||||
<h3 className="text-heading-3 text-ink">角色权限</h3>
|
||||
<p className="mt-sm text-small text-ink-muted">暂无角色数据</p>
|
||||
<section className="rounded-xl border border bg-card p-4">
|
||||
<h3 className="text-heading-3 text-foreground">角色权限</h3>
|
||||
<p className="mt-sm text-sm text-muted-foreground">暂无角色数据</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="rounded-card border border-rule bg-surface p-md">
|
||||
<h3 className="text-heading-3 text-ink">角色权限</h3>
|
||||
<div className="mt-sm flex gap-md">
|
||||
<section className="rounded-xl border border bg-card p-4">
|
||||
<h3 className="text-heading-3 text-foreground">角色权限</h3>
|
||||
<div className="mt-sm flex gap-4">
|
||||
{/* 左侧角色列表 */}
|
||||
<div className="w-64 flex-shrink-0">
|
||||
<p className="text-small text-ink-muted">角色</p>
|
||||
<p className="text-sm text-muted-foreground">角色</p>
|
||||
<ul className="mt-xs space-y-xs">
|
||||
{roles.map((role) => {
|
||||
const isSelected = role.id === effectiveRoleId;
|
||||
@@ -102,10 +102,10 @@ export default function RbacManager(_props: PluginProps): React.ReactElement {
|
||||
type="button"
|
||||
onClick={() => setSelectedRoleId(role.id)}
|
||||
aria-pressed={isSelected}
|
||||
className={`w-full rounded-button border px-sm py-xs text-left text-small ${
|
||||
className={`w-full rounded-md border px-sm py-xs text-left text-sm ${
|
||||
isSelected
|
||||
? "border-accent bg-accent-subtle text-ink"
|
||||
: "border-rule bg-paper text-ink"
|
||||
? "border-accent bg-primary-subtle text-foreground"
|
||||
: "border bg-background text-foreground"
|
||||
}`}
|
||||
>
|
||||
{role.name}
|
||||
@@ -119,19 +119,19 @@ export default function RbacManager(_props: PluginProps): React.ReactElement {
|
||||
{/* 右侧权限矩阵 */}
|
||||
<div className="flex-1 overflow-x-auto">
|
||||
{permissions.length === 0 ? (
|
||||
<p className="text-small text-ink-muted">暂无权限数据</p>
|
||||
<p className="text-sm text-muted-foreground">暂无权限数据</p>
|
||||
) : (
|
||||
<table className="w-full text-tiny">
|
||||
<table className="w-full text-xs">
|
||||
<thead>
|
||||
<tr className="border-b border-rule text-ink-muted">
|
||||
<tr className="border-b border text-muted-foreground">
|
||||
<th className="py-xs text-left">权限</th>
|
||||
{roles.map((role) => (
|
||||
<th
|
||||
key={role.id}
|
||||
className={`py-xs text-center ${
|
||||
role.id === effectiveRoleId
|
||||
? "text-ink"
|
||||
: "text-ink-muted"
|
||||
? "text-foreground"
|
||||
: "text-muted-foreground"
|
||||
}`}
|
||||
>
|
||||
{role.name}
|
||||
@@ -141,10 +141,10 @@ export default function RbacManager(_props: PluginProps): React.ReactElement {
|
||||
</thead>
|
||||
<tbody>
|
||||
{permissions.map((perm) => (
|
||||
<tr key={perm.id} className="border-b border-rule">
|
||||
<tr key={perm.id} className="border-b border">
|
||||
<td className="py-xs">
|
||||
<p className="text-ink">{perm.name}</p>
|
||||
<p className="text-tiny text-ink-muted">
|
||||
<p className="text-foreground">{perm.name}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{perm.resource} / {perm.action}
|
||||
</p>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user