25 KiB
Frontend React / Vite / Design
Source Of Truth
Frontend visual and interaction design uses the installed frontend-design
skill as the primary design authority. docs/产品设计规则.md remains a product
experience and business-boundary fact source, not a visual, brand or aesthetic
rulebook. For PC/mobile/WeCom pages, add Playwright or visual verification when
reasonable.
This spec keeps hyb-oa-specific React/Vite, shared-component, business-boundary
and verification contracts. Do not copy broad aesthetic guidance from
frontend-design into this file; use the skill directly for design judgment.
Rules
- Use existing React/Vite/Tailwind patterns and shared components before adding page-local UI systems.
- When
frontend-designand older project visual rules conflict, followfrontend-designfor visual direction while preserving security, privacy, Trellis, deployment and business data boundaries. - Default product framing is a small-team Chinese workbench for domestic tech
startups with fewer than 10 people. User-facing copy should be natural
Chinese; avoid English product words such as
Portal,DashboardandWorkflowunless they are internal test fixtures or technical identifiers. - PC pages should keep dense but orderly OA layouts: clear header, filters, table/list, status, details and predictable actions.
- Mobile pages must reuse the existing unified mobile shell, card/list rows,
safe-area and sticky action patterns unless the current task intentionally
redesigns them with
frontend-design. - Do not add decorative landing-page styling to operational OA pages.
- Run
cd client && npm run typecheck && npm run buildfor frontend changes.
Frozen / Boundary Items
Frozen modules and Enterprise WeChat-owned HR/attendance entries must be labeled or hidden according to the current Trellis task; do not silently re-enable them.
- Use the shared
WorkspaceNoticepattern for trial boundaries, safety notes, and post-trial capabilities instead of page-local warning blocks. - Trial-facing PC lists must hide obvious TEST/SMOKE records by default when a page-level toggle exists; the toggle may reveal them for troubleshooting.
Scenario: Business Flow Tests And Frozen Modules
1. Scope / Trigger
- Trigger: Playwright or QA shell tests need to create workflow data while some product modules are frozen for launch.
- Applies to
client/tests/e2e/**,scripts/qa/run-business-flow-gates.sh, and frontend route/permission assertions.
2. Signatures
- QA command:
BUSINESS_FLOW_GROUPS=<groups> bash scripts/qa/run-business-flow-gates.sh local. - Frozen-module API expectation: non-admin launch roles may receive
403for frozen module list/create/submit endpoints. - Enabled workflow test data sources should use active entities such as leave, reimbursement or procurement unless the task explicitly reopens the frozen module.
3. Contracts
- Do not use a frozen module as fixture setup for an unrelated enabled feature. Example: mobile approval offline sync should not create a travel request when travel permissions are intentionally frozen.
- Permission direct-access tests must assert the frozen contract directly, such
as employee
GET /api/travel-requests?...returning403. - UI route smoke may still verify that a frozen page displays a boundary state without entering a management or write-capable mode.
- Do not loosen roles or seed permissions merely to make old E2E data setup pass.
4. Validation & Error Matrix
- Frozen role calls frozen module create/submit/list endpoint ->
403or the documented feature-gate status. - Enabled workflow feature needs pending task data -> create data through an enabled workflow entity.
- E2E fails with
PERM_001while setting up unrelated feature data -> update the test data source before changing app permissions.
5. Good/Base/Bad Cases
- Good: mobile approval offline test creates a submitted leave request, then verifies pending-task deep link, offline queue, sync success and conflict.
- Base: travel-specific tests are skipped or admin-scoped while travel remains frozen.
- Bad: grant employee
travel.createonly so a mobile approval test can build a fixture.
6. Tests Required
- Focused E2E group that previously failed.
- Full affected business-flow group after the focused fix.
cd client && npm run typecheck && npm run buildwhen TypeScript test code changes.
7. Wrong vs Correct
Wrong
await createSubmittedTravelRequestViaApi({ user: EMPLOYEE });
Correct
await createSubmittedLeaveViaApi({ user: EMPLOYEE });
Convention: PC Operation List Density
What: PC list/workflow pages that combine status metrics, tabs/filters,
range controls, export and table/list content should reuse the shared operation
classes in client/src/index.css:
ops-status-stripfor compact status metrics.ops-command-bar,ops-command-bar__row,ops-command-bar__tabs,ops-command-bar__actionsandops-filter-labelfor tabs, filters and secondary actions.ops-table-focusfor the primary desktop table container.ops-inline-alertfor temporary mode hints such as batch operation notices.PrimaryTablealready appliesops-table-focusby default; page code may add responsive visibility classes such ashidden md:block, but should not recreate a separate table frame.- Finance/admin status summaries should prefer the shared
finance-status-stripandfinance-metric-cardclasses unless the task introduces a new reusable metric component.
Why: This keeps high-frequency PC pages dense and predictable without
recreating page-local Tailwind command bars. It also lets frontend-design
change the visual treatment once while preserving component contracts.
Example:
<StatStrip columns={4} className="ops-status-strip">
<MetricCard title="待办总数" value={summary.pendingTotal} />
</StatStrip>
<FilterBar className="ops-command-bar !p-3 md:!p-3.5">
<div className="ops-command-bar__row">
<Tabs value={status}>
<TabsList className="ops-command-bar__tabs !h-auto !justify-start !bg-slate-50">
<TabsTrigger value="pending">待处理</TabsTrigger>
</TabsList>
</Tabs>
<div className="ops-command-bar__actions">...</div>
</div>
</FilterBar>
<PrimaryTable className="hidden md:block">...</PrimaryTable>
Tests Required:
cd client && npm run typecheckcd client && npm run build- Focused Playwright screenshot coverage when changing login, Portal, primary PC list pages or command-bar/table styling.
Convention: Global Shell And Page Header Baseline
What: PC page redesigns should preserve the shared Wave0-2 shell and header baseline instead of recreating page-local wrappers:
AppShellLayoutowns the PC shell background and content offset throughapp-shellandapp-shell-main.SidebarandNavLinksown role-aware task navigation. The mental model is a short primary task list plus collapsed更多; management configuration is represented by one管理entry instead of many sidebar rows.Topbaris a compact command surface: page title, group badge, user context, global search and notifications. Do not expand it back into a large hero.PageHeaderandWorkspaceHeadershould usepage-header-shell,page-header-shell__eyebrow,page-header-shell__title,page-header-shell__subtitleandpage-header-shell__actions.- Auth guidance and step hints may use
ux-command-note,ux-step-listandux-step-itemwhen they explain the next action without adding page-local warning cards.
Why: The shell/header baseline gives Wave3-5 pages a stable visual entry point and prevents each route from inventing a different title/action layout.
Navigation Rules:
- Sidebar entries should be role-aware and task-first for domestic tech startups under 10 people; do not expose all permission-allowed modules as a default module catalog.
- Approvers see
待我处理; ordinary employees should not see待办or审批待办in the sidebar by default. - PC leave navigation uses
请假记录unless the current task explicitly opens OA emergency leave submission. 个人设置stays in the topbar account menu. Notifications stay in the topbar bell and notification center.- Finance labels should use work names such as
付款,应付账款and票据; internal platform words likeAP 运营,ERP 集成and功能开关should be hidden behind management wording or renamed for users. 更多is a lightweight collapsed row for low-frequency entries; do not add a sidebar explanation card, subtitle or onboarding copy around it.- Playwright navigation helpers should use exact names or anchored regex for
short sidebar labels such as
管理, otherwise fuzzy role-name matching can click longer labels such as预算管理.
Example:
<WorkspaceHeader
eyebrow="审批"
title="审批中心"
subtitle="先看待处理,再进入详情核对依据。"
/>
Tests Required:
cd client && npm run typecheckcd client && npm run build- Focused Playwright coverage when changing
AppShellLayout,Sidebar,Topbar,NavLinks,PageHeader,WorkspaceHeader, login, Portal or other A-class pages.
Convention: A-Class PC Task-First Pages
What: A-class PC pages such as Portal, approvals, reimbursement list and reimbursement detail should make the current user task obvious before exposing secondary operations:
- Treat the primary persona as a small-team operator wearing multiple hats, not a dedicated OA clerk. The first screen should reduce decision cost for a founder, finance/admin owner, project lead or employee who may switch roles during the same session.
- First visible task surface should answer: what needs attention, what can be started, and where recent progress lives.
- Keep one primary action in the first screen. Allow at most two secondary
entries; move export, batch, delete, templates, complex filters, statistics
and admin/finance tools into
更多,高级 / 台账, detail actions, role-only panels or confirmation dialogs. - Do not turn small-team workflows into platform-builder surfaces by default: approval designers, broad configuration matrices, multi-level enterprise dashboards and batch admin consoles need explicit Trellis task scope.
- Explanation text should only be default-visible for risk, exception or empty states. Do not use repeated subtitles or notice cards to explain normal workflow steps that the layout can express.
- Low-frequency capabilities are hidden or moved, not removed, unless the current Trellis task explicitly deletes the capability.
- Portal/workbench first screen should use a compact status summary plus
今日待处理; do not use a persistent welcome hero, fixed报销进度button, general announcement feed, or folded placeholder sections such as团队与通知,角色重点or试点观测摘要. - Workbench sections after
今日待处理must be conditional:团队卡点only for real blockers,管理提醒only for real admin/trial issues, and我的进度only for reliable status updates. Empty data should disappear, not render as collapsed drawers or statistic cards. - Each workbench action row should state what the item is, why the current user is involved, current status/timing, and one next action. Do not represent raw application counts as progress unless the payload includes an actionable or status-level signal.
- PC reimbursement list defaults to the initiator's own forms even for users
with finance/approval roles. The first screen may use task cards for
rejected corrections, drafts and reviewing progress; ledger/export/batch,
proxy submit, templates and travel-linking remain secondary and must be hidden
unless the current user has a real matching capability. Row workflow copy
should distinguish
待你处理from read-only当前节点.
Why: hyb-oa serves small teams with overlapping roles. Core pages must make the next click clear for ordinary employees and approvers, while keeping finance and admin tools available without dominating the default path.
Tests Required:
cd client && npm run typecheckcd client && npm run build- Focused Playwright or screenshot checks for changed A-class PC pages, asserting the task surface, primary action and secondary/advanced entry behavior.
Convention: Approval Task List Defaults
What: PC approval task lists such as /workflow/tasks should make
查看处理 the default row action. List-level 通过 is only a secondary shortcut
for low-risk items where the row itself carries enough decision context.
Contracts:
- The row must answer what the item is, who requested it, why it is assigned to the current user, how long it has waited, and the public business reference.
- History entry text should be explicit, such as
已处理记录; avoid user-facing高级 / 历史on the default task surface. - Time labels should prefer
已等待多久and接收于over database-shaped labels such as更新时间when the source field is task creation/arrival time. - A quick
通过button may appear only when the task is pending, the server allowsapprove, the item is a low-risk leave/attendance task, required row summary fields are present, and the task has not exceeded the pending window available to the frontend. Reimbursement, procurement, travel, contract and other amount/evidence-heavy tasks should route through查看处理first. - The confirm dialog for list quick approval must still explain action consequences and include the specific task summary.
Why: Small-team approvers need to clear work quickly without training themselves to approve financial or evidence-heavy items from an under-specified row.
Convention: Workbench Onboarding And Profile Scope
What: PC workbench and account settings should stay task-first, not become training or permission-maintenance surfaces:
- Do not add a persistent
快速上手/ onboarding checklist to the workbench by default. If first-login guidance is needed, use a dismissible welcome dialog with at most three real business entries. - Welcome or onboarding entries must point to executable user tasks, such as
处理待办,提交报销or请假; do not use explanatory entries like了解流程, and do not route users to settings or notifications as a substitute for task guidance. 个人设置default content is account identity, notification preference and Enterprise WeChat binding. Organization, report line and role summary may be folded under账号详情; permission code lists and full employee dossier fields stay out of the default view.
Why: Small teams need fewer default decisions. Persistent onboarding and permission-heavy profile pages make ordinary users inspect system internals before doing work.
Tests Required:
- Focused Playwright coverage for changed workbench or profile routes.
- Screenshot evidence for first-login welcome, workbench first screen and profile default view when changing these surfaces.
Convention: Detail And Mobile Command Surfaces
What: PC detail pages and notification-like flows should reuse the Wave3
surface classes in client/src/index.css before adding page-local Tailwind
card systems:
detail-workspace-gridfor the main PC detail two-column workspace.detail-command-panel,detail-fact-gridanddetail-fact-cardfor compact status, summary, payment, export and evidence facts.detail-evidence-cardfor attachments, invoices and other proof material.detail-timelineanddetail-timeline-itemfor approval progress and history.detail-action-stackanddetail-action-cardfor approve/reject/return, payment and other high-risk command groups.notification-inbox,notification-row-cardandnotification-row-card--unreadfor PC notification streams; avoid reverting notifications to a desktop table unless a task explicitly requires tabular comparison.
Mobile key pages should reuse the mobile command classes in
client/src/styles/mobile-touch.css:
mobile-focus-cardfor the current task, current list or account summary.mobile-command-surfacefor tabs, search and filters.mobile-list-card--focusandmobile-list-card--unreadfor state emphasis.
Why: Detail pages should feel like quiet command surfaces: status, summary, primary action, evidence and history have predictable places. Mobile pages should stay card/list based, with no PC tables, no horizontal overflow and no button text squeeze.
Tests Required:
cd client && npm run typecheckcd client && npm run build- Focused Playwright screenshot coverage for changed PC detail, notification and mobile pages.
- UI design rules check when changing shared visual classes.
Scenario: Auth Pages And PWA Updates
1. Scope / Trigger
- Trigger: login, password change, password recovery or other unauthenticated forms are active while the PWA service worker finds a new frontend version.
- Applies to
client/src/pwa.ts,client/src/layouts/AuthLayout.tsx, and auth routes such as/login,/change-password,/forgot-password, and future reset-password pages.
2. Signatures
- Event name:
hyb-oa:pwa-update-ready. - Event detail:
{ applyUpdate?: () => void | Promise<void> }. - Route predicate: auth input pages must be checked by pathname before calling
updateServiceWorker(true).
3. Contracts
- Auth input pages must not auto-reload when
onNeedRefresh()fires. - Auth input pages must show an explicit update prompt with a user-triggered refresh action.
- Non-auth pages may keep automatic service-worker update behavior when the task does not change broader runtime policy.
- Passwords and verification codes must stay only in React component state; do
not write them to
localStorage,sessionStorage, URL params, reports, or Trellis task evidence. - Password visibility controls must use icon buttons with accessible labels and preserve the current input value.
4. Validation & Error Matrix
- PWA update on
/login-> dispatch update-ready event and keep form state. - PWA update on
/forgot-password-> dispatch update-ready event and keep email/code/password fields in memory. - User clicks refresh in the prompt -> run the captured update handler.
- User dismisses prompt -> no reload and no form reset.
- Password reset request returns success for any email -> show generic sent message; do not reveal whether the account exists.
5. Good/Base/Bad Cases
- Good: user is typing a password, update prompt appears, typed values remain.
- Base: user is on a normal portal page, PWA update can refresh per existing runtime behavior.
- Bad: service-worker update auto-refreshes an auth form and clears credentials.
- Bad: tests use real-looking personal email addresses or persist example passwords outside the test file.
6. Tests Required
- Frontend typecheck and build.
- Focused Playwright auth UX test asserting:
- login password can be shown/hidden without losing value;
- forgot-password flow calls request-code and reset endpoints;
- auth-page PWA update prompt appears without clearing inputs.
- UI design rules check when auth page layout changes.
- Any Playwright test that renders
/login,/forgot-passwordor another auth route with mocked/api/*responses must mock/api/auth/capabilitieswithemailDelivery.ready,emailLoginCode.enabledandpasswordReset.enabled. Returning{}for unknown auth APIs can crash auth pages before the visual or navigation assertion actually runs.
7. Wrong vs Correct
Wrong
onNeedRefresh() {
void updateServiceWorker?.(true);
}
Correct
onNeedRefresh() {
if (isAuthInputPath(window.location.pathname)) {
window.dispatchEvent(new CustomEvent(PWA_UPDATE_READY_EVENT, { detail: { applyUpdate } }));
return;
}
applyUpdate();
}
Scenario: Auth Page Layout And Login Entry Hierarchy
1. Scope / Trigger
- Trigger: login, password recovery, forced password change or future unauthenticated auth forms need layout changes.
- Applies to
client/src/layouts/AuthLayout.tsx, shared auth components underclient/src/components/auth/, and auth routes such as/login,/forgot-passwordand/change-password.
2. Component Contract
- Auth routes should use one shared auth panel for brand, page title, subtitle, content and optional footer links.
AuthLayoutowns only the auth page shell: background, width constraint, centering, PWA update prompt and route outlet.- Page implementations own only their form fields, submit actions, validation messages and route-specific login-entry choices.
3. Visual Contracts
- Do not nest a form card inside another login card.
- Put the brand block inside the auth panel so logo, system name, title and form share one visual axis.
- Keep the panel width stable around the current auth-shell width; auth pages should feel like an OA workstation entry, not a marketing split hero.
- Place
忘记密码?beside the password label or directly below the password input on the right; do not bury it in help text or page footer. - Keep the DOM/tab order aligned with the main form flow even when CSS places
忘记密码?visually beside the password label: username/email input -> password input -> secondary controls. Do not render the forgot-password link before the password input in DOM order. - Use password visibility icon buttons with accessible labels and preserve the current input value.
4. Login Entry Hierarchy
- Normal browser: account/password login is primary; Enterprise WeChat and OIDC entries belong below the primary submit button as secondary methods.
- Enterprise WeChat WebView: Enterprise WeChat one-click entry is primary; account/password login remains available as the fallback path.
- Enterprise WeChat entry changes must remain frontend entry hierarchy changes unless the task explicitly approves backend OAuth, binding or WeCom API work.
- Email verification-code login is a separate login mode. Do not infer it from password-recovery verification-code flows without a backend login-code contract and abuse-control plan.
5. Tests Required
- Frontend typecheck and build.
- Focused auth E2E covering:
- password visibility and forgot-password navigation on
/login; - keyboard Tab from the username/email field lands on the password input, not the forgot-password link;
- password recovery request-code and reset flow on
/forgot-password; - normal-browser Enterprise WeChat entry as secondary CTA;
- WeCom WebView Enterprise WeChat entry as primary CTA with password fallback.
- password visibility and forgot-password navigation on
- UI design rules check and Playwright screenshot evidence when the auth layout changes visibly.
6. Wrong vs Correct
Wrong
<AuthLayout>
<Card>
<Card>
<LoginForm />
</Card>
</Card>
</AuthLayout>
Correct
<AuthPanel title="登录工作台" subtitle="账号密码登录">
<LoginForm />
<SecondaryLoginMethods />
</AuthPanel>
Wrong
<Label htmlFor="login-password">密码</Label>
<Link to="/forgot-password">忘记密码?</Link>
<PasswordInput id="login-password" />
Correct
<Label htmlFor="login-password">密码</Label>
<PasswordInput id="login-password" />
<Link className="row-start-1" to="/forgot-password">忘记密码?</Link>
Convention: Finance And Admin Console Surfaces
What: PC finance and C-class admin pages should reuse Wave4 shared classes
from client/src/index.css before creating page-local card/table layouts:
finance-status-strip,finance-metric-card,finance-metric-card__labelandfinance-metric-card__valuefor compact five-column status summaries.finance-workspace-grid,finance-command-panel,finance-list-cardandfinance-list-card--activefor payment/AP/invoice operational workspaces.admin-command-panel,admin-tile-grid,admin-tile-cardandadmin-workspace-gridfor settings, workflow, feature-flag, user, master data, notification-channel and audit pages.
Why: Finance/admin pages are lower-frequency but high-risk operational surfaces. They need dense scanability, stable command regions and clear risk hierarchy without turning every page into a bespoke Tailwind card system.
Example:
<div className="finance-status-strip" data-testid="payment-batches-summary">
<div className="finance-metric-card">
<div className="finance-metric-card__label">可处理</div>
<div className="finance-metric-card__value">{actionableCount}</div>
</div>
</div>
<div className="finance-workspace-grid">
<section className="finance-command-panel">...</section>
<section className="finance-command-panel">...</section>
</div>
Tests Required:
cd client && npm run typecheckcd client && npm run build- Focused Playwright coverage for changed finance/admin pages. The Wave4
baseline test is
client/tests/e2e/pc-finance-admin-visual-wave4.spec.ts. - UI design rules check when changing these shared classes.