From b26001e039eaee51a3588f06194e0db9d8c72457 Mon Sep 17 00:00:00 2001 From: inman Date: Wed, 2 Sep 2026 16:59:53 +0800 Subject: [PATCH] fix: prevent account permission editor occlusion --- ...20260902-account-permission-ui-6c9e21ad.md | 67 +++++++++++++++++++ LianSyn-platform/app.js | 4 +- LianSyn-platform/index.html | 4 +- LianSyn-platform/styles.css | 10 +-- .../test/account-authorization.test.ts | 27 ++++++++ control-plane/test/control-plane.test.ts | 4 +- 6 files changed, 107 insertions(+), 9 deletions(-) create mode 100644 .project-docs/30-worklog/tasks/20260902-account-permission-ui-6c9e21ad.md diff --git a/.project-docs/30-worklog/tasks/20260902-account-permission-ui-6c9e21ad.md b/.project-docs/30-worklog/tasks/20260902-account-permission-ui-6c9e21ad.md new file mode 100644 index 0000000..f345c9c --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260902-account-permission-ui-6c9e21ad.md @@ -0,0 +1,67 @@ +# Task: Diagnose account task-permission UI occlusion + +## Identity + +- Task ID: 20260902-account-permission-ui-6c9e21ad +- Mode: Feature +- Branch: codex/20260902-account-permission-ui-6c9e21ad-account-permission-ui +- Worktree: /Users/inmanx/Documents/lwltAPI-account-permission-ui-6c9e21ad +- Base commit: a618d3dd9fd8aa4e6f9a22e81b9b65452185b86a +- Owner: codex +- Status: Ready for integration + +## Scope + +- Diagnose the reported account-management UI defect where clicking “任务权限” reveals an editor that is obscured by the account list. +- Trace the account-management DOM, click handler, CSS grid cascade, overflow behavior, and existing focused tests. +- Reproduce the final computed layout in an isolated headless browser using only repository HTML/CSS/JavaScript and synthetic non-sensitive account/task-type data. +- Implement the user-authorized layout correction, add regression coverage, refresh the platform asset cache token, commit the completed change, and push the feature branch to the configured remote. +- Do not change permission semantics, APIs, runtime services, database state, ERP state, deployment state, or other external systems. + +## Intent And Constraints + +- Preserve the accepted fixed-scope three-role authorization behavior and all 18 route-grant semantics; the implementation is limited to authorization-editor presentation and its regression coverage. +- Use active source at base commit `a618d3dd9fd8aa4e6f9a22e81b9b65452185b86a`; do not treat archive material as current authority. +- Do not read `.env`, use real credentials, inspect real account data, or access the standard service. Browser reproduction must use local static assets and synthetic data only. +- Keep all repository operations rooted in the isolated owned worktree and leave the occupied main worktree unchanged. + +## Outcome + +- Confirmed that the button event is functioning: clicking “任务权限” sets the authorization target, renders all 18 task types, and changes `#accountAuthorizationPanel` from hidden to visible. +- Identified the root cause as a CSS grid cascade conflict. `LianSyn-platform/styles.css` defines the intended five-row account layout at `.account-panel` (`auto auto auto minmax(0, 1fr) auto`), but the later and more specific `.channels-page .channel-panel` rule replaces it with the generic four-row channel layout (`auto auto minmax(0, 1fr) auto`). Specificity is `0-1-0` versus `0-2-0`, so source order cannot rescue the account-specific rule. +- The account panel has five visible children while the authorization editor is open: heading, create form, authorization editor, account list, and message. Under the winning four-row layout, the editor takes the flexible third row and the account list takes the auto-sized fourth row. As the list's intrinsic height grows, the editor row collapses to zero; its descendants overflow into the list's area, and the later-painted account list receives pointer hits over the editor. +- `scrollIntoView()` does not resolve the defect: in the reproduced failure the account page had identical client and scroll heights, while the editor descendants were geometrically present underneath the account list. +- The defect is viewport- and account-count-dependent. At `1440×800`, three accounts already truncate the editor and four accounts collapse it to a 0 px track; at `1920×1080`, collapse begins at six accounts; at `390×844`, one account truncates it and two accounts collapse it. +- A browser-only temporary override restoring an account-specific five-row track removed the overlap and made the task-type label the top hit-tested element. No repository source was changed by that experiment. +- Existing authorization tests verify that the controls and handlers exist, but do not render the open editor or assert geometry, overlap, visibility, or pointer hit behavior; therefore the focused suite remains green despite the defect. +- Implemented the authorized correction by toggling an `is-authorizing` class from `renderAccountAuthorizationPanel()`. The closed account page retains its existing compact four-row layout; only the open editor switches to a five-row, `height: max-content` account-specific layout whose full content is scrolled by `#accountsPage`. +- Removed the ineffective lower-specificity `.account-panel` grid override so the winning cascade is explicit and testable, and placed the open-state rule after the generic management-page rule. +- Updated both platform asset URLs to cache token `20260902-account-authorization-layout-1`, preventing a cached JavaScript or stylesheet from masking the fix. +- Added a focused source regression that verifies the open-state class toggle, the scoped rule's cascade order and scroll-safe declarations, the absence of the stale weak override, and synchronized asset tokens. + +## Verification + +- `agent-browser` local static reproduction at `1440×800` with 18 synthetic task types and 8 synthetic accounts: computed account rows were `72.1875px 55px 0px 460.812px 0px`; the visible editor box was only 26 px high, its first task-type label occupied `y=359.4375..423.4375`, and `document.elementFromPoint()` at that label returned the account list's authorization summary instead of the label. +- Same reproduction measured a 12 px editor/list box overlap and `accountsPage.scrollHeight === accountsPage.clientHeight === 678`, confirming the content could not be recovered by page scrolling. +- Browser-only five-row override: editor/list overlap became 0 and hit-testing returned `.account-authorization-type.is-selected`. +- `/Users/inmanx/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node --test control-plane/test/account-authorization.test.ts`: 10 passed, 0 failed. This validates the current static contract while demonstrating the missing layout regression. +- `git blame` attributes the generic four-row management-page rule to the original baseline and the lower-specificity account five-row rule to account-system commit `191c1a1a`; the account UI addition introduced the unhandled cascade collision. +- `check_project_docs.py`: passed. +- `check_doc_drift.py --task-id 20260902-account-permission-ui-6c9e21ad`: passed; only the owned task record changed. +- `node --run check:repo`: 10 passed, 0 failed. +- `node --run check`: passed. +- `node --run test:control-plane`: 159 passed, 0 failed. +- `node --run test:legacy`: 265 passed, 0 failed. +- `node --run build`: passed. +- Focused account-authorization suite after the fix: 11 passed, 0 failed. +- `git diff --check`: passed. +- `agent-browser` desktop regression at `1440×800` with 8 synthetic accounts and 18 task types: closed state retained four rows and fixed-height list scrolling; open state used five rows, expanded `#accountsPage.scrollHeight` from 678 to 1603, kept editor/list overlap at 0, and returned `.account-authorization-type.is-selected` from hit-testing. Clicking an unselected checkbox changed it to checked, and Cancel restored the closed four-row layout. +- `agent-browser` narrow regression at `390×844` with 4 synthetic accounts and 18 task types: open state expanded the page scroll height to 1625, editor/list overlap remained 0, horizontal scroll width equaled client width at 370, and task-type hit-testing returned the authorization label. + +## Follow-ups + +- Merge/integration and any runtime rollout remain separate from this feature-branch push; no service restart, deployment, real account mutation, or ERP activity was performed. + +## Promotion Candidates + +- None recorded. diff --git a/LianSyn-platform/app.js b/LianSyn-platform/app.js index 18d3750..68a59f5 100644 --- a/LianSyn-platform/app.js +++ b/LianSyn-platform/app.js @@ -1087,7 +1087,9 @@ function renderAccountAuthorizationPanel() { const container = $('#accountAuthorizationTypes'); if (!panel || !container) return; const account = accountList.find((item) => item.id === accountAuthorizationTargetId); - if (!account || account.role === 'admin') { + const isOpen = Boolean(account && account.role !== 'admin'); + panel.closest('.account-panel')?.classList.toggle('is-authorizing', isOpen); + if (!isOpen) { panel.hidden = true; container.replaceChildren(); accountAuthorizationTargetId = ''; diff --git a/LianSyn-platform/index.html b/LianSyn-platform/index.html index 784b7a6..a1297f0 100644 --- a/LianSyn-platform/index.html +++ b/LianSyn-platform/index.html @@ -5,7 +5,7 @@ AI操作台 · LianSyn-platform - +
@@ -407,6 +407,6 @@
- + diff --git a/LianSyn-platform/styles.css b/LianSyn-platform/styles.css index db2d4f6..9332713 100644 --- a/LianSyn-platform/styles.css +++ b/LianSyn-platform/styles.css @@ -634,10 +634,6 @@ textarea { font-size: 11px; } -.account-panel { - grid-template-rows: auto auto auto minmax(0, 1fr) auto; -} - .account-authorization-summary { color: var(--ink) !important; } @@ -3049,6 +3045,12 @@ textarea { grid-template-rows: auto auto minmax(0, 1fr) auto; } +.channels-page .channel-panel.account-panel.is-authorizing { + height: max-content; + grid-template-rows: auto auto auto auto auto; + overflow: visible; +} + .channels-page .channel-list { max-height: none; } diff --git a/control-plane/test/account-authorization.test.ts b/control-plane/test/account-authorization.test.ts index a0f5965..232f836 100644 --- a/control-plane/test/account-authorization.test.ts +++ b/control-plane/test/account-authorization.test.ts @@ -339,3 +339,30 @@ test('operator UI exposes role-aware accounts, executive drill-through, original assert.doesNotMatch(retention, /DELETE FROM tasks/); assert.doesNotMatch(retention, /DELETE FROM audit_events/); }); + +test('account authorization editor uses a scroll-safe open layout without overriding the closed account list', async () => { + const [app, index, styles] = await Promise.all([ + source('../../LianSyn-platform/app.js'), + source('../../LianSyn-platform/index.html'), + source('../../LianSyn-platform/styles.css') + ]); + const renderStart = app.indexOf('function renderAccountAuthorizationPanel()'); + const renderEnd = app.indexOf('function openAccountAuthorizationEditor(', renderStart); + const renderSource = app.slice(renderStart, renderEnd); + assert.match(renderSource, /const isOpen = Boolean\(account && account\.role !== 'admin'\)/); + assert.match(renderSource, /classList\.toggle\('is-authorizing', isOpen\)/); + assert.match(renderSource, /if \(!isOpen\)/); + + const genericLayout = styles.indexOf('.channels-page .channel-panel {'); + const openLayout = styles.indexOf('.channels-page .channel-panel.account-panel.is-authorizing {'); + assert.ok(genericLayout >= 0, 'generic management-page layout is present'); + assert.ok(openLayout > genericLayout, 'account open-state layout follows the generic layout in the cascade'); + const openLayoutSource = styles.slice(openLayout, styles.indexOf('}', openLayout) + 1); + assert.match(openLayoutSource, /height:\s*max-content/); + assert.match(openLayoutSource, /grid-template-rows:\s*auto auto auto auto auto/); + assert.match(openLayoutSource, /overflow:\s*visible/); + assert.doesNotMatch(styles, /\.account-panel\s*\{\s*grid-template-rows:/); + + assert.match(index, /styles\.css\?v=20260902-account-authorization-layout-1/); + assert.match(index, /app\.js\?v=20260902-account-authorization-layout-1/); +}); diff --git a/control-plane/test/control-plane.test.ts b/control-plane/test/control-plane.test.ts index 615b334..904e50d 100644 --- a/control-plane/test/control-plane.test.ts +++ b/control-plane/test/control-plane.test.ts @@ -1231,8 +1231,8 @@ test('operator page has a login gate and uses the durable task API', async () => const inpage = await readFile(new URL('../../chrome-extension/ltjt-order-assistant/inpage.js', import.meta.url), 'utf8'); assert.match(index, /id="loginPanel"/); assert.match(index, /id="workbench"[^>]*hidden/); - assert.match(index, /styles\.css\?v=20260902-agentbus-dashboard-metrics-filter-1/); - assert.match(index, /app\.js\?v=20260902-agentbus-dashboard-metrics-filter-1/); + assert.match(index, /styles\.css\?v=20260902-account-authorization-layout-1/); + assert.match(index, /app\.js\?v=20260902-account-authorization-layout-1/); assert.match(index, /id="statusDetailsPopover"/); assert.match(index, /id="statusDetailsRefresh"/); assert.match(app, /apiRequest\(`\/api\/tasks\?\$\{params\.toString\(\)\}`/);