Commit Graph

10 Commits

Author SHA1 Message Date
SpecialX
7c511e74bd feat(portal-shell): add CI structural checks for routes, pages, codegen (P1-8)
ARCHITECTURE.md §10 P1-8: three structural checks wired into CI to
prevent regressions identified in the §1.3 audit.

Scripts (apps/portal-shell/scripts/):
- check-route-table.ts: scans src/app/shell/**/page.tsx, parses
  route-permissions.ts (EXACT/PREFIX/DASHBOARD/PUBLIC_ROUTES), fails
  if any actual /shell/* route is unregistered. Reports ghost entries
  (EXACT declarations without page.tsx) as informational.
- check-page-count.ts: asserts total page.tsx >= 13 and per-category
  minimums (dashboards/login/root/forbidden/catch-all/dev-templates).
- check-codegen.ts: runs pnpm run codegen, fails if any output with
  skipDocumentsValidation:false has operations referencing non-existent
  schema fields (currently enforces dashboard-types.ts output from P1-7).

npm scripts: check:routes / check:pages / check:codegen / check:all

CI: .github/workflows/ci.yml quality-ts job — new "Portal-shell
structural checks (P1-8)" step between typecheck and test.

Acceptance (ARCHITECTURE.md §10 P1-8 — "CI 对预埋违规报红"):
- Route violation: planted /shell/test-violation/page.tsx → check:routes
  exits 1 with "unregistered route" error; reverted → PASS
- Codegen violation: planted non_existent_field in GetTeacherDashboard →
  check:codegen exits 1 with "Cannot query field" error; reverted → PASS
- Page count: baseline=13, deleting any page.tsx triggers FAIL
- Clean state: all 3 checks PASS (10 routes, 28 EXACT, 24 ghost entries
  informational, 13 pages, codegen 3 outputs SUCCESS)

Refs: ARCHITECTURE.md §5.3, §10 P1-8, §11.6, §11.7 红线 #5
2026-07-22 15:57:58 +08:00
SpecialX
0beeff6329 feat(portal-shell): restore codegen typescript-operations for data-ana domain (P1-7)
ARCHITECTURE.md §10 P1-7: dashboard domain's 6 operations strictly
match the schema, so disable skipDocumentsValidation for that output
and restore per-operation type generation.

Changes:
- codegen.yml: add dashboard-types.ts output (typescript +
  typescript-operations plugins, skipDocumentsValidation: false);
  move documents config into each generates entry
- dashboard.ts: remove 14 handwritten interfaces and 6 internal query
  type aliases; derive types via NonNullable<GetXxxQuery['xxx']> so
  the public hook API shape stays unchanged
- admin/student/teacher page.tsx: add ?? "--" / ?? 0 null guards on
  StatCard value props to match schema nullable semantics (parent page
  already uses toFixed chain, no change needed)

Acceptance (ARCHITECTURE.md §10 P1-7):
- codegen 3 outputs all SUCCESS
- tsc 0 errors / eslint 0 errors / vitest 231 passed / next build ok
- 6 operations strictly match schema with 0 errors

Refs: ARCHITECTURE.md §5.3 data layer / §10 P1-7
2026-07-22 15:37:06 +08:00
SpecialX
a28a6bd6ea feat(portal-shell): clean widget design tokens and fix lint:tokens (P1-6)
516 mechanical replacements across 25 widget files:
- spacing xs/sm/md/lg/xl to numeric 1/2/3/4/6
- text-heading-3 to text-lg font-semibold
- bg-danger to bg-destructive
- border border dedup

Fix .eslintrc.tokens.js to use typescript-eslint parser (was importing
uninstalled @typescript-eslint/parser). lint:tokens now passes.
2026-07-22 15:07:38 +08:00
SpecialX
9358372657 feat(portal-shell): add MSW mock layer with production bundle exclusion (P1-5)
MSW v2.7.0 fallback layer covering dashboard/users/exams/grades domains.
NEXT_PUBLIC_MSW=1 enables browser Service Worker + SSR route handler mock
responses without backend. Production build excludes all mock data via
Turbopack resolveAlias redirecting @/mocks to empty stub.

Acceptance: build bundle (client+server) verified clean of mock strings;
typecheck/lint/vitest (231 tests) all pass.
2026-07-22 14:48:30 +08:00
SpecialX
da05c9107a feat(portal-shell): integrate next-intl + merge messages from teacher-portal (P1-4)
- Add next-intl v4.13.2 with cookie-based locale (no i18n routing)
- Create src/i18n/request.ts reading NEXT_LOCALE cookie
- Merge zh-CN/en messages from teacher-portal + add shell.dev.templates namespace
- Wrap next.config.js with withNextIntl plugin (Turbopack resolveAlias)
- Refactor RootLayout to async + NextIntlClientProvider + getLocale/getMessages
- Replace ThemeI18nProvider with ThemeProvider (theme-only, i18n removed)
- Remove locale/setLocale from PluginStore
- Rework locale-switcher to useLocale/useTranslations + router.refresh
- Update dev/templates page to use getTranslations (Server Component)
- Fix WorkbenchPageShell test (loading prop + center instead of children)

Verified: locale switch via NEXT_LOCALE cookie changes <html lang> and messages
zh-CN: 保存/取消/切换侧栏 | en: Save/Cancel/Toggle sidebar
typecheck 0 errors | lint 0 errors 2 warnings (generated) | vitest 231 passed
2026-07-22 13:40:44 +08:00
SpecialX
994441c2dc feat(portal-shell): add page templates quartet (list/detail/form/workbench) (P1-3)
P1-3 验收通过:4 个页面模板 + 4 个 dev 示例页 + 三态规范。

新增文件:
- src/shared/components/page-templates/
  - list-page.tsx:ListPageShell + ListPageSkeleton
  - detail-page.tsx:DetailPageShell + DetailSection + DetailField + DetailPageSkeleton
  - form-page.tsx:FormPageShell + FormPageSkeleton
  - workbench-page.tsx:WorkbenchPageShell + WorkbenchPanel + WorkbenchPageSkeleton
  - index.ts:barrel 导出
- src/app/shell/dev/templates/
  - page.tsx:索引页(4 个模板入口)
  - list/page.tsx:列表页示例(支持 ?state=loading|empty|success)
  - detail/page.tsx:详情页示例
  - form/page.tsx:表单页示例
  - workbench/page.tsx:工作台页示例
- src/shared/components/__tests__/page-templates.test.tsx:19 个单测

修改文件:
- src/shared/lib/route-permissions.ts:新增 PREFIX /shell/dev/(空 config = 仅校验登录)
- ARCHITECTURE.md:P1-3 状态回填  + 验收证据

路径命名修正:
- 原 ARCHITECTURE.md 写 /shell/_dev/templates/*,但 Next.js 将下划线开头的
  文件夹视为"私有文件夹"(不参与路由),实测被 [[...route]] catch-all 兜底接管。
- 改用 dev 命名后,显式路由优先匹配,catch-all 不再触发。

三态规范验证:
- GET /shell/dev/templates/list?state=loading → 200,含 animate-pulse 骨架
- GET /shell/dev/templates/list?state=empty → 200,含"暂无数据"空态
- GET /shell/dev/templates/list(默认 success)→ 200,含表格数据

质量校验:
- tsc --noEmit 通过
- eslint(新/改文件)通过
- vitest run 全量 21 test files / 231 tests 全部通过(212 原有 + 19 新增)

Refs: apps/portal-shell/ARCHITECTURE.md §7.3 页面四种类型与模板、
      §7.4 页面级数据获取模式、§11.3 每页硬性清单(DoD)三态规范
2026-07-22 13:02:11 +08:00
SpecialX
98058eb16b feat(portal-shell): wire dashboards to real data-ana queries (P1-2)
- add dashboard.graphql.ts with 6 real aggregate queries
  (teacherDashboard / studentDashboard / parentDashboard /
   adminDashboard / warnings / errorBookStats), snake_case aligned
- add dashboard.ts with 6 hooks + full domain model types
- add 4 role dashboard pages (teacher/student/parent/admin)
  using DashboardShell + StatCard + DashboardSection with
  loading / error / success tri-state
- update [[...route]]/page.tsx to redirect /shell -> /shell/{role}
- retire 6 fake contract queries and hooks (grades/homeworks/
  schedule/attendance/exams/announcements) and mark widget
  placeholders as migrated
- update universal.test.ts to drop retired hook tests
- mark ARCHITECTURE.md P1-2 as completed with acceptance evidence
2026-07-22 12:33:22 +08:00
SpecialX
f92fdf8efe feat(portal-shell): wire AppFrame + navigation + permission filter (P1-1)
实现 ARCHITECTURE.md §10 P1-1:
- 新增 src/shared/lib/navigation.ts:27 项静态导航注册表,
  按 teacher/student/parent/admin 四角色分区,group 字段用于角色过滤
- 新增 src/app/shell/layout.tsx:RSC AppFrame,从 headers() 读取
  middleware 注入的身份头,batchCheckRoutePermission 按位图二次过滤
- 新增 src/shared/components/layout/user-menu.tsx:顶部用户菜单,
  显示 userId + role,登出 POST /api/auth/logout
- 新增 src/shared/lib/__tests__/navigation.test.ts:P1-1 验收单测
  (7 用例:href 登记一致性 + 4 角色隔离 + 权限检查)
- 修改 src/shared/lib/route-permissions.ts:补全 7 个列表页根路由
  的 EXACT 登记(/shell/admin/announcements、/shell/admin/classes、
  /shell/teacher/exams 等),与 PREFIX 表互补避免 catch-all 拒绝

验收:
- vitest run navigation → 7/7 passed
- tsc --noEmit 通过;eslint(5 文件)通过

回填 ARCHITECTURE.md §10 P1-1 状态为 ,附验收证据。

关联:ARCHITECTURE.md §7.2 AppFrame / §10 P1-1
2026-07-22 12:17:16 +08:00
SpecialX
dc13a2afb4 docs(portal-shell): backfill ARCHITECTURE.md §10 P0 completion status
- §10 P0 表格新增"状态"列,全部标记 
- 新增"P0 验收证据(2026-07-22)"小节,逐条附命令/文件引用
- 文档头部状态行从"待实施"改为
  "P0 已完成 + P1 进行中"
- 验收依据:
  P0-1 login API 401 实测
  P0-2 middleware + forbidden page 代码
  P0-3 localStorage 仅匹配删除注释
  P0-4 getDefaultConfig 实现
  P0-5 instrumentation register() 实现
  P0-6 vitest 27/27 通过
  P0-7 eslint 0 errors
  P0-8 git status 干净
2026-07-22 11:42:02 +08:00
SpecialX
cfb7b005fd feat(portal-shell): v2.1 P0 auth + middleware + login + graphql proxy
- 新增 ARCHITECTURE.md v3.0:portal-shell 架构权威文档
  涵盖 §3.4 V3-A2/A3 认证链、§4 GraphQL 联邦、§5 安全、
  §6 部署、§10 P0-P3 验收清单
- 新增 middleware.ts:认证 + 路由门禁
  httpOnly cookie edu_session(JWT)读取
  DEV_MODE 合成 dev-user/teacher 身份(NODE_ENV!=production &&
  NEXT_PUBLIC_DEV_MODE=true)
  生产模式 jose JWKS RS256 验签(iss/aud 校验)
  路由权限位图注入 x-user-id/x-user-role/x-user-permissions 头
  /shell/** 强制 checkRoutePermission,拒绝跳 /shell/forbidden
- 新增 instrumentation.ts:生产环境 DEV_MODE 强制 false
  防止生产环境误开 DEV_MODE 合成身份
- 新增 app/api/auth/login/route.ts + logout/route.ts
  登录走 api-gateway /v1/iam/login
  设置 httpOnly + Secure + SameSite=Strict cookie
- 新增 app/api/graphql/route.ts:同域 GraphQL 代理
  转发到 apollo-router,注入 router-authorization 头
- 新增 app/login/page.tsx + login-form.tsx
  zod 表单校验,next 参数支持
- 新增 app/shell/forbidden/page.tsx:403 页面
- 更新 route-permissions.ts:补全 P0 路由权限映射
- 更新 permission-bitmap.ts(shared-ts):位图编码/解码
- 更新 apollo-client.ts:DEV_MODE APQ 关闭,错误处理
- 更新 config-fetcher.ts:config-service 直连降级
- 更新 ApolloProvider.tsx:SSR/RSC 兼容
- 更新 eslint.config.js:design-tokens/no-hardcoded-fonts
  白名单调整
2026-07-22 11:35:36 +08:00