Skip to content

Commit b97af7e

Browse files
os-zhuangclaude
andauthored
feat(platform-objects): surface sys_user admin actions on the record-detail header (#2846)
* feat(platform-objects): surface sys_user admin actions on the record-detail header The account-management actions (Ban/Unban, Unlock Account, Set Password, Set Platform Role, Impersonate) were declared `locations: ['list_item']` only, so an admin had to return to the Users list to run them — they were unavailable from an open user record. Add `record_header` to each so they also appear in the detail header's ⋯ "More" menu. objectui's record-detail page already renders `record_header` actions (overflows extras past the 3rd into the "More" menu), injects `userId` from the viewed record, and drives the param/confirm/result dialogs — so no objectui change is needed. `record_header` is the only detail-surface location objectui consumes (it does not read `record_more`), hence that specific string. Adds a regression assertion in platform-objects.test.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: add changeset Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 261aff5 commit b97af7e

3 files changed

Lines changed: 32 additions & 8 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@objectstack/platform-objects': patch
3+
---
4+
5+
`sys_user` account-management actions (Ban/Unban, Unlock Account, Set Password, Set Platform Role, Impersonate) now also surface on the user record-detail header (`record_header`, overflowing into the ⋯ "More" menu), not just the Users list row menu — so a platform admin can manage an account from an open user record without navigating back to the list.

packages/platform-objects/src/identity/sys-user.object.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,17 @@ export const SysUser = ObjectSchema.create({
7777
// These actions hit /api/v1/auth/admin/* endpoints that are only
7878
// wired when `auth.plugins.admin` is enabled. When the plugin is
7979
// disabled the actions still render (schema is static) but server
80-
// returns 404. UI surfaces them under the row menu so platform
81-
// admins can manage accounts without dropping to SQL or
80+
// returns 404. UI surfaces them under the row menu AND the
81+
// record-detail header (`record_header`, overflowing into the ⋯
82+
// "More" menu) so platform admins can manage an account from either
83+
// the Users list or an open user record — without dropping to SQL or
8284
// a custom Setup wizard.
8385
{
8486
name: 'ban_user',
8587
label: 'Ban User',
8688
icon: 'ban',
8789
variant: 'danger',
88-
locations: ['list_item'],
90+
locations: ['list_item', 'record_header'],
8991
type: 'api',
9092
target: '/api/v1/auth/admin/ban-user',
9193
recordIdParam: 'userId',
@@ -101,7 +103,7 @@ export const SysUser = ObjectSchema.create({
101103
label: 'Unban User',
102104
icon: 'check-circle-2',
103105
variant: 'secondary',
104-
locations: ['list_item'],
106+
locations: ['list_item', 'record_header'],
105107
type: 'api',
106108
target: '/api/v1/auth/admin/unban-user',
107109
recordIdParam: 'userId',
@@ -116,7 +118,7 @@ export const SysUser = ObjectSchema.create({
116118
label: 'Unlock Account',
117119
icon: 'lock-open',
118120
variant: 'secondary',
119-
locations: ['list_item'],
121+
locations: ['list_item', 'record_header'],
120122
type: 'api',
121123
target: '/api/v1/auth/admin/unlock-user',
122124
recordIdParam: 'userId',
@@ -185,7 +187,7 @@ export const SysUser = ObjectSchema.create({
185187
label: 'Set Password',
186188
icon: 'key-round',
187189
variant: 'secondary',
188-
locations: ['list_item'],
190+
locations: ['list_item', 'record_header'],
189191
type: 'api',
190192
// #2766 V1 — same path as better-auth's stock route, but served by the
191193
// plugin-auth wrapper registered ahead of the catch-all: it accepts the
@@ -228,7 +230,7 @@ export const SysUser = ObjectSchema.create({
228230
label: 'Set Platform Role',
229231
icon: 'shield-check',
230232
variant: 'secondary',
231-
locations: ['list_item'],
233+
locations: ['list_item', 'record_header'],
232234
type: 'api',
233235
target: '/api/v1/auth/admin/set-role',
234236
recordIdParam: 'userId',
@@ -243,7 +245,7 @@ export const SysUser = ObjectSchema.create({
243245
label: 'Impersonate User',
244246
icon: 'user-cog',
245247
variant: 'secondary',
246-
locations: ['list_item'],
248+
locations: ['list_item', 'record_header'],
247249
type: 'api',
248250
target: '/api/v1/auth/admin/impersonate-user',
249251
recordIdParam: 'userId',

packages/platform-objects/src/platform-objects.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,23 @@ describe('@objectstack/platform-objects', () => {
190190
'dnsRecordValue',
191191
]);
192192
});
193+
194+
it('SysUser admin actions surface on the record-detail header, not just the row menu', () => {
195+
// An admin viewing an open user record must be able to run the same
196+
// account-management actions available from the Users list row menu —
197+
// otherwise they have to navigate back to the list. Each admin action is
198+
// therefore declared on BOTH `list_item` and `record_header` (the detail
199+
// header overflows extras into the ⋯ "More" menu). `record_header` is the
200+
// only detail-surface location objectui consumes (it does NOT read
201+
// `record_more`), so these must use `record_header` specifically.
202+
const adminActions = ['ban_user', 'unban_user', 'unlock_user', 'set_user_password', 'set_user_role', 'impersonate_user'];
203+
for (const name of adminActions) {
204+
const a = (SysUser.actions ?? []).find((x) => x.name === name);
205+
expect(a, `${name} action must exist`).toBeTruthy();
206+
expect(a?.locations, `${name} locations`).toContain('list_item');
207+
expect(a?.locations, `${name} must also surface on the detail header`).toContain('record_header');
208+
}
209+
});
193210
});
194211

195212
describe('SETUP_APP (ADR-0029 D7 shell)', () => {

0 commit comments

Comments
 (0)