Repository navigation
fix(ui,platform-objects): invitation screens — labelled statuses, role help, resend as a confirmation, dialog focus and email check - #28224
Conversation
…aram dialog checks and focus, joined state Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibyekmZuiE5TVrwDWqH
…e help, resend as a confirmation, param dialog format check and focus Invitation statuses carry display labels. Every role picker that grants an organization role explains each role on its own line, and Change Role names the member. Resend confirms with the row's email and role carried over. The workspace cancel dialog dismisses with Keep invitation. The console's param dialog checks email and url params against the form's format rule and marks the field. The param, confirm and result dialogs open on the first editable field and return focus to their opener. The sign-in page tells an invitee without the link to open it, and the accept page says the user joined before Continue enters the organization. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibyekmZuiE5TVrwDWqH
…as it turned disabled A browser blurs a focused button when it becomes disabled, so an action button that disables itself while its action runs leaves focus on body by the time its dialog opens. The dialog now takes the control the user last pressed or clicked as its opener in that case. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibyekmZuiE5TVrwDWqH
📓 Docs Drift CheckThis PR changes 1 package(s): 34 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: ⛔ 12 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails. What this run could not see
Coarse fallback — 2 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e660e5c39e4cc131898a8aac30b1aa07a530e1c0 && git checkout e660e5c39e4cc131898a8aac30b1aa07a530e1c0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 1ea3fe9eaa8cf43657cb66b119f6ed15ac43d19c 5b5f464ebfb64b208e1ef14f0971917ae689e12e && git checkout -B drift-repro 1ea3fe9eaa8cf43657cb66b119f6ed15ac43d19c && git merge --no-ff 5b5f464ebfb64b208e1ef14f0971917ae689e12e
node scripts/docs-audit/affected-docs.mjs --json 1ea3fe9eaa8cf43657cb66b119f6ed15ac43d19c
|
|
domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: Contract review lane (Fable): declaration face (Clause-②: yes, no schema or governed path) at TIER_DEFAULT per |
|
domain:ui#1 · os-tesla · session_01L9qibyekmZuiE5TVrwDWqH
Generated by Claude Code |
…k included The console's pin evaluates this exact sentence through a real ActionRunner, including the fallback for a row that does not expand user_id; holding the whole declaration here keeps that copy from drifting. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01L9qibyekmZuiE5TVrwDWqH
|
domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract-review-claim: |
|
domain:ui#1 · os-tesla · session_01L9qibw58ehQ2gRHP2h2Puz Contract reviewServed-tier: Inputs: card #27521 body (rows 7, 8, 9, 10, 11, 23) and its thirteen comments (triage 6495068090, seat 3's split 6511102307 and claim 6511168770, seat 2's claim 6511270445, reports 6512376770 and 6513177556, seat 3's accept 6512877469, seat 2's landing 6513658747 and release 6524603067, ui seat 1 claim 6526176786 and amendments 6526355417 and 6529299111, os-dev-report 6529206941); PR #28224 body, 39-file list and the diff against Delta since the 2582296 review (claim 6529782482; re-claimed on this head): two commits. fa9836b merges origin/main 39da1bd (which carries the #28227 main-red fix): the PR's own hunks against the new merge base are byte-identical to the reviewed ones (39 files, same names, no content difference) and the merge touches no file the PR touches. 5b5f464 is test-only: ① Derived judgments
② Semver level
③ Boundary flags
Should-fix, resolved by 5b5f464 on this head (the test now holds the whole sentence): Implemented-by: VERDICT: PASS |
|
domain:ui#1 · os-tesla · session_01L9qibzC6RxMfB59EsgAoNN 席位核对ACCEPT · seat 席位意见:Setup → Invitations 的六行建议中,这个 PR 做了能做的部分。邀请状态改显示标签(Pending / Accepted / Declined …),不再是原始小写值。所有授予角色的选择框共用一段说明,每个角色一行;Change Role 的说明会点名被改的成员。Resend 改为确认框,不再弹出可编辑的表单。控制台的动作对话框会先检查邮箱/网址参数,错误标在字段上;打开时焦点落在第一个可编辑字段,关闭后焦点回到打开它的按钮。在只能凭邀请注册的环境里,登录页会提示受邀人去打开邀请链接;接受邀请后,页面会告诉用户已加入哪个组织、担任什么角色。余下三项留在卡上(见第 3 条),所以本 PR 是
|
Requested by Tesla · project thread
Part of #27521
Clause-②: yes (widening:
@object-ui/app-shellexportsInvitationLinkHintand@object-ui/plugin-formexportsuseInitialFieldFocus; no accept set moves)Metadata-protocol: none (platform object metadata only, no authorable key:
sys_invitation.statusoptions gain display labels, withrejectedread as "Declined"; the role param of the fourinvite_useractions,sys_member.add_memberandsys_member.update_member_rolegains one sharedhelpText;sys_invitation.resend_invitationgains adescriptionand carries its email and role over from the row;sys_member.update_member_rolegains adescriptionnaming the member; theexpiredinvitations list projectsrole)This PR lands rows 7, 8, 9, 10 (the invite half), 11 and 23 of the 18.3.0 RC walkthrough, minus two pieces that need a decision or a spec member (see Acceptance notes). It keeps the card open.
维护者速读
ui/packages/app-shell/src/views/actionDialogReturnFocus.ts(焦点归还,尤其等待被禁用按钮恢复的那段);ui/packages/app-shell/src/views/ActionParamDialog.tsx(提交时先查必填再查格式);packages/platform-objects/src/identity/sys-invitation.object.ts(重发改为带入行上的 email/role)。pnpm --dir ui exec vitest run packages/app-shell/src/views/actionDialogFocus-27521.test.tsx packages/app-shell/src/views/ActionParamDialog.invitationScreens-27521.test.tsx,预期 2 个文件、18 个用例全过。What changes
Platform objects (
@objectstack/platform-objects)sys_invitation.statusoptions carry labels (Pending, Accepted, Declined, Expired, Canceled), so list cells read the label instead of the raw value. Theexpiredlist view also projectsrole, as the other views do.MEMBERSHIP_ROLE_PARAM_HELPin the newidentity/membership-role-help.ts, explains each built-in membership role on its own line. It is built fromBUILTIN_MEMBERSHIP_ROLE_OPTIONS, so a role and its label cannot drift. All six role pickers that grant an organization role use it: the fourinvite_useractions (onsys_invitation,sys_member,sys_user,sys_organization),add_memberandupdate_member_role.update_member_rolegains a description that names the member, with a fallback for a row that does not expand the user.resend_invitationbecomes a confirmation. Its description names the invitee and says the expiry starts over, and its email and role params carry over from the row, so the dialog shows no editable inputs.Shared action dialogs (
@object-ui/app-shell)The change moves three shared dialogs that every declared action uses:
ActionParamDialog,ActionConfirmDialogandActionResultDialog.useConsoleActionRuntimeis unchanged.Row 10:
ActionParamDialogchecks anemailorurlparam against the same format rule the object form uses (buildValidationRulesfrom@object-ui/fields) before anything runs. A malformed value is marked on its field (message under it,aria-invalid), and the run never starts, so the old banner plus toast cannot appear. An empty required field still reads "X is required". Carried-over values and empty optional values are not judged.Row 8: help text under a param keeps its line breaks (
whitespace-pre-line), so the role help reads one role per line.Row 23, opening: the param dialog opens on its first editable field instead of the footer's Cancel. It reuses the object form's
useInitialFieldFocus, now exported from@object-ui/plugin-form. A dialog of carried-over values only opens on itself.Row 23, closing: the new
actionDialogReturnFocus.tsgives all three dialogsuseActionDialogReturnFocus. When a dialog closes, focus goes back to the control that opened it. This covers three openers that defeated Radix's default:With no opener (focus on
bodyand no recent activation), Radix's default stays.Invitation pages (
@object-ui/app-shell,@object-ui/console)InvitationLinkHintlives with the other invitation-arrival pieces and is rendered by both sign-in pages, the app-shell default and the console's own. After accepting, the accept page shows "Invitation accepted", "Welcome to ORG. You joined as ROLE." and a focused Continue button that enters the organization, instead of a toast and an immediate redirect.Tests
packages/platform-objects/src/identity/invitation-screens-copy.test.ts(new) pins:BUILTIN_MEMBERSHIP_ROLE_OPTIONSlabel, and that all six pickers carry it;ui/packages/app-shell/src/views/ActionParamDialog.invitationScreens-27521.test.tsx(new, 10 cases), under a real EnglishI18nProvider:paramFormatErrorKeyskips carried-over and empty values;ActionRunnerfrom a list row.ui/packages/app-shell/src/views/actionDialogFocus-27521.test.tsx(new, 8 cases):ui/packages/i18n/src/__tests__/invitation-screens-27521.test.ts(new): all ten packs carry the four keys, translated, with the English placeholders.defaultPagesFollowPosture-11705,signUpFollowsPosture-11691), "Keep invitation" inorg-i18n-holdouts-4474, and the joined state plus Continue inAcceptInvitationRoute(its toast assertion is replaced).Acceptance notes
sys_invitation.statusoptions had no labelsrejectedreads "Declined"invitation-screens-copy.test.tsupdate_member_rolehad no descriptionwhitespace-pre-line; Change Role names the memberinvitation-screens-copy.test.ts,ActionParamDialog.invitationScreens-27521.test.tsxresend_invitationdeclared editable params; the workspace cancel dialog used a generic Cancelinvitation-screens-copy.test.ts,org-i18n-holdouts-4474.test.tsxActionParamDialogchecked only required, so the server's refusal showed twiceActionParamDialog.invitationScreens-27521.test.tsxInvitationLinkHinton both sign-in pages; a joined state with ContinueAcceptInvitationRoute.test.tsx,invitation-screens-27521.test.tsactionDialogFocus-27521.test.tsxWhat is left on the card, so this PR says
Part of:needs_decisionagainst ADR-0138.Live check (private stack,
invite_onlyposture, Chrome):aria-invalid=trueand no toast. The role help reads one role per line. Both Escape and a successful invite return focus to Invite User.Verification
Local runs at the branch head before the last merge of
main(a78dee0). The last merge, at the head, brings 10 commits that touch none of this diff's files. Both the platform-objects suite and the touched ui suites were re-run after the previous merge.Tests and type-checks
pnpm --filter @objectstack/platform-objects exec vitest run: Test Files 104 passed (104), Tests 1474 passed (1474).pnpm --dir ui exec vitest runover the packages this diff moves and their consumers): Test Files 944 passed (944), Tests 10202 passed, 15 skipped.@object-ui/i18n,@object-ui/plugin-form,@object-ui/app-shell,@object-ui/consoleand@objectstack/platform-objects.node scripts/check-sdui-manifest.mjs: exit 0.node scripts/check-i18n-bundles.mjsafter a platform-objects closure build: OK, no file changes.Ablations (each restored, with `git diff HEAD` empty)
node scripts/ablation-replace.mjs), the browser-blur case goes red: 1 failed, 7 passed (8).Derived gates: 64 derived, 64 run, all exit 0; reconciled
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran Fprinted "64 derived famil(ies) accounted for — 64 run, 0 NOT-MEASURED".check:i18nfirst exited 1 after mergingmain, because the platform-objects dist was stale. After a closure rebuild it exited 0 with no file changes. The plugin-teardown-shape self-test needs its pinned commit, which this shallow clone fetched first.The full farm runs in CI.
Generated by Claude Code