Skip to content

feat(users): Profile tab, dialog shell & list CRUD wiring (#36717) - #36990

Open
AP2300 wants to merge 3 commits into
mainfrom
issue-36717-users-portlet-profile-tab
Open

feat(users): Profile tab, dialog shell & list CRUD wiring (#36717)#36990
AP2300 wants to merge 3 commits into
mainfrom
issue-36717-users-portlet-profile-tab

Conversation

@AP2300

@AP2300 AP2300 commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Closes #36717

Summary

  • Ships the Users portlet Create/Edit dialog shell (4-tab strip, header, footer) with the Profile tab fully wired against the backend and inline "Coming soon" placeholders for the Roles, Permissions, and API Tokens tabs (delivered by sibling issues Users portlet: Roles tab (dual-list) #36718, Users portlet: Permissions tab (iframe embed) #36719, Users portlet: API Tokens tab #36720)
  • Adds the missing user CRUD to the list flow: GET /api/v1/users/{userId} hydrates the edit dialog, POST /api/v1/users creates, PUT /api/v1/users updates (roles field intentionally omitted so backend preserves membership — see UserResource#processRoles)
  • Delete flow — both from the dialog footer and the list bulk toolbar — now includes a required replacement-user picker (server-backed autocomplete over /api/v1/users/filter, excludes the users being deleted). Previously deletes silently reassigned ownership to the logged-in admin.
  • Fixes /users routing so the beta portlet actually lands on this UI instead of bouncing to Getting Started (PORTLET_ID_RESOLVERS.usersusers-beta).

Notable non-obvious calls

  • Access section is display-only in this PR: CMS Admin, Back-end, Front-end, Can Login map to backend roles (not UserForm fields), and safely editing them requires knowing the user's full role list. That's a follow-up (DWR wrapper vs. small REST endpoint decision).
  • Password stays optional on update. Empty password field means "keep current" — matches how UserForm handles password already; no client-side hack needed.
  • Show Getting Started toggle has no backend field; kept in the UI as informational, not persisted. To be revisited if the value is needed elsewhere.

Test plan

  • Create user via the "New" button — required fields validated, password ≥ 6 chars, confirm-match enforced; on Save a new row appears in the list
  • Edit user via row click — dialog hydrates from list-item data instantly, then background-fetches additionalInfo (prefix/suffix/title/company/website); on Save, changes persist and the list reloads
  • Toggle Active on/off via the Profile field — persists on save
  • Delete user via the dialog footer — replacement picker required, cannot pick the same user; typed-email confirmation required; on confirm, list reloads
  • Bulk-delete N users from the toolbar — replacement picker required, cannot pick any of the selected; on confirm, list reloads
  • Placeholder tabs render "Coming soon" and don't interfere with Profile Save
  • pnpm nx test portlets-dot-users-portlet — 56 passing
  • pnpm nx test data-access --testPathPatterns=dot-router — 46 passing
  • pnpm nx lint portlets-dot-users-portlet — clean

Follow-ups (each on its own branch/PR already prepared)

🤖 Generated with Claude Code

Ships the Users portlet Create/Edit dialog shell with the Profile tab
fully wired and placeholders for the three sibling tabs (delivered by
#36718, #36719, #36720).

Dialog / Profile tab:
- 4-tab strip with Profile as the only functional tab; Roles,
  Permissions, and API Tokens render "Coming soon" placeholders
- Header with avatar + name + Active status chip
- Account section: first/last name, email, password + confirm, Active
- Additional Info section: prefix/suffix/title/company/website
- Access section: disabled (values informational only), shows admin /
  backend / frontend / hasConsoleAccess pulled from the loaded user
- Delete User section (edit mode) with required replacement-user
  picker and email-typed confirmation

List CRUD:
- DotUsersService gains getUser/createUser/updateUser using
  POST/PUT/GET /api/v1/users; roles field intentionally omitted on
  update so backend preserves role membership (see
  UserResource#processRoles)
- DotUsersListStore gains createUser / updateUser / deleteSingleUser
- Bulk delete on the list toolbar now shows the same replacement
  picker instead of the old p-confirmDialog
- /users route now resolves to the new users-beta portlet id

Shared:
- DotUsersReplacementPickerComponent — server-backed autocomplete
  used by both delete flows; excludes the users being deleted from
  suggestions client-side

Test coverage: 56 tests in the portlet + 46 in data-access all pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the Area : Frontend PR changes Angular/TypeScript frontend code label Aug 10, 2026
AP2300 and others added 2 commits August 11, 2026 14:00
…ints

Extends the Profile tab so the Access toggles and Show Getting Started
now round-trip against the backend, and polishes a couple of visual
loose ends caught during review.

Access section:
- CMS Administrator / Back-end User / Front-end User toggles hydrate
  from `GET /api/v1/roles/users/{userId}` and persist through the
  `roles` field on `PUT /api/v1/users`. The load response is cached
  as the full role-key list so save can strip only the three
  access-role keys and preserve every other membership (personal
  role, project roles, etc.).
- Save button stays disabled until roles + gettingStarted have
  finished loading, so an accidental click cannot ship an incomplete
  role list.
- Can Login to Admin UI stays disabled but its value is now derived
  live from `cmsAdmin || backend` — reactively updated via
  valueChanges. Matches the legacy behavior of read-only display
  driven by role membership.

Show Getting Started:
- New GET /api/v1/toolgroups/gettingstarted/_userHasLayout on load,
  and PUT _addtouser / _removefromuser on save when the toggle
  diverged from the initial state. Chained off the primary save via
  switchMap in the store — a toolgroup failure surfaces via the
  shared error manager but does not fail the user save.

Password field:
- Adds a masked `••••••••` placeholder on the password / confirm
  fields in edit mode so the empty state visually communicates
  "a password is set". Leave-blank-to-keep-current behavior is
  unchanged.

Password field UX cleanup:
- Header subtitle in create mode ("Fill in the details below to
  create this user") removed along with its i18n key; the avatar
  + name are enough context in the header.

List polish:
- Empty Roles and Last-login cells now render blank instead of the
  `—` placeholder.

Test coverage: 61 portlet tests + 46 data-access tests pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Addresses M. Rojas's review thread (ts 1786391221.355219) on the
Profile tab and delete flows.

Delete User section (Profile tab):
- Drops the red-tinted border + background; the section now uses the
  same neutral treatment as the other Account / Access cards. The
  Delete button itself keeps `severity="danger"` since that's the
  actual destructive action.

Delete confirm dialogs (both single-user + bulk):
- Replacement picker now fills the field width via `styleClass="w-full"`
  + `inputStyleClass="w-full"` on the shared component.
- Removes the disabled-Delete pattern per the design convention of
  keeping buttons enabled. Clicking with an invalid form now surfaces
  field-level errors — red outline via PrimeNG's `[invalid]` on the
  picker + `ng-invalid/ng-dirty` classes on the type-to-confirm input,
  plus inline error text below each field.
- Granular error keys (`replacement.required`, `replacement.self`,
  `confirm.required`, `confirm.mismatch`) replace the earlier footer
  summary — users see exactly which field needs attention.
- Picker gains a passthrough `[invalid]` input so callers can toggle
  the underlying p-autoComplete error state without reaching into the
  shared component internals.

Profile save form:
- Required-field error text under firstName, lastName, email,
  password, and confirmPassword — chained via `@if / @else if` so
  each field surfaces the most relevant error (required → format →
  cross-field mismatch).
- Explicit `[invalid]` (p-password) and `[class.ng-invalid]` /
  `[class.ng-dirty]` (pInputText) bindings driven by helper predicates
  on the profile tab. `markAllAsTouched()` in save() sets touched
  without dirty, so PrimeNG's default `.ng-invalid.ng-dirty` styling
  wasn't picking up the touched-only state.

Users list empty state:
- New SCSS override forces the p-datatable scroll body, table, and
  tbody to `height: 100%` so the empty message row can actually
  stretch to the tbody's computed height. Previously the tr/td auto-
  sized to content, leaving the icon+title+description pinned to the
  top instead of centered.

Test coverage: 61 tests + lint clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
oidacra added a commit that referenced this pull request Aug 11, 2026
Read the design project directly rather than working from the issue summary.
The list row was missing two controls it specifies.

- New Experiment button in the toolbar. Creation lands with its own screen
  (#36990+), so it renders disabled with a tooltip rather than being absent.
- Primary row action leading the Actions cell: "View Results" where results
  exist, "Configure" otherwise. Derived from
  `AllowedActionsByExperimentStatus.results` (RUNNING, ENDED) rather than
  restating the statuses. Disabled for now: AC10 forbids routing into the
  legacy UVE screens until Screens 2 and 3 land, so the row matches the design
  without being able to navigate.
- Variants centred and Actions right-aligned, per the design.

Column order already matched: the design has no Modified column, and the issue
asked for "the design's columns + Modified", which is what ships.

Also drops the `dot-experiment-status-tag` wrapper: a component whose only job
was to render one `p-tag` earned nothing over inlining the tag and deriving
severity, icon and label on the row. Its severity coverage moved to the list
spec, so the mapping is still asserted for all five statuses.

Reuses `experiments.action.view.results`; adds keys only where the design's
copy genuinely differs from an existing string.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area : Frontend PR changes Angular/TypeScript frontend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Users portlet: Profile tab (Create and Edit dialog)

1 participant