Skip to content

refactor(ui): centralize device & container action logic#6573

Merged
gustavosbarreto merged 1 commit into
masterfrom
feat/use-device-actions
Jun 30, 2026
Merged

refactor(ui): centralize device & container action logic#6573
gustavosbarreto merged 1 commit into
masterfrom
feat/use-device-actions

Conversation

@luannmoreira

Copy link
Copy Markdown
Member

What

Accept/reject/remove dialog wiring and the billing-warning gate were duplicated across DeviceDetails, ContainerDetails, and both list pages. This extracts a single shared implementation that all four pages consume.

Why

Closes shellhub-io/team#153. Any bug in the action flow, loading state, or error handling previously had to be fixed in each page independently. (The audit named three files; the real duplication spans four — admin/devices has no action logic, while ContainerDetails does.)

Changes

  • hooks/useActionDialogState: generic state core — operation state, requestAction, close, the cloud-gated onBillingWarning, and runSuccess(action). All returned callbacks are useCallback-stable so list-page column useMemos don't churn.
  • hooks/useDeviceActions / useContainerActions: thin typed wrappers (the issue-named public API); enableBillingWarning defaults to !!getConfig().cloud.
  • DeviceActionsPortal / ContainerActionsPortal: render the existing *ActionDialog + BillingWarning from the controller, preserving the key-based reset and the entity→device/container prop mapping.
  • Pages: DeviceDetails, devices/index, ContainerDetails, containers/index drop their inline state/render in favor of the hook + portal. ContainerDetails passes enableBillingWarning: false to keep its existing no-billing-warning flow.
  • The *ActionDialog and BillingWarning components are unchanged — they already centralized the mutations and 402 handling.

Testing

  • New unit tests: useActionDialogState (state, billing gating, stable refs, action forwarding) and both portals (prop mapping; ContainerActionsPortal asserts BillingWarning is absent when enableBillingWarning: false).
  • Existing page-test mocks were retargeted from the dialogs to the new portals.
  • Full console suite: 2597 passing; eslint and tsc -b clean.
  • Reviewer note: confirm ContainerDetails still shows the inline 402 error (not the billing dialog) on a cloud build — that asymmetry vs. the other pages is intentional and preserved.

@luannmoreira luannmoreira requested a review from a team as a code owner June 29, 2026 22:08
@luannmoreira luannmoreira self-assigned this Jun 29, 2026
@github-actions

github-actions Bot commented Jun 29, 2026

Copy link
Copy Markdown

Claude finished @luannmoreira's task in 5m 51s —— View job


Code Review Complete

Reviewed 15 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is.

Highlights from the review:

  • The useActionDialogStateuseDeviceActions/useContainerActions → portal layering is clean and the billing-warning gate is preserved correctly in all four consuming pages.
  • ContainerDetails correctly passes enableBillingWarning: false to preserve its existing inline-error flow.
  • The DeviceDetails migration from the standalone ConfirmDialog (for accepted-device delete) to the unified DeviceActionDialog portal is safe — DeviceActionDialog fully handles the remove action via the same useRemoveDevice mutation.
  • No cross-repo impact found in cloud/ — no references to removed components.
  • All useCallback-stable refs are correctly maintained, and the key-based remount strategy in both portals prevents stale-closure issues.

If you push additional changes and want a new review, tag @shellhub-io/admin and a team member can trigger it.

Comment thread ui/apps/console/src/hooks/__tests__/useActionDialogState.test.ts Outdated
Comment thread ui/apps/console/src/hooks/useActionDialogState.ts Outdated
luannmoreira added a commit that referenced this pull request Jun 30, 2026
Drop the unused TEntity/TAction generics from useActionDialogState — both
domains always use {uid, name} entities and the accept/reject/remove action
set, so the hook now operates on the concrete EntityBase/EntityAction types.
The action dialogs reuse the shared EntityBase instead of redefining it.

The useActionDialogState test mock now spreads env's defaultConfig rather
than re-declaring the full ClientConfig literal.
@otavio

otavio commented Jun 30, 2026

Copy link
Copy Markdown
Member

/review

@github-actions

github-actions Bot commented Jun 30, 2026

Copy link
Copy Markdown

Claude finished @otavio's task in 11m 54s —— View job


Code Review Complete

Reviewed 17 files across code quality, security, testing, language patterns, and architecture — no new issues found. The code looks good as-is.

Highlights from the review:

  • The useActionDialogStateuseDeviceActions/useContainerActions → portal layering is clean. The ref-based onSuccess pattern (sync ref after every render, stable runSuccess callback) correctly avoids stale-closure and identity-churn issues.
  • Both portals correctly capture operation.action at render time for the onSuccess closure, so the cancel-race scenario (user closes dialog while mutation is in-flight) is handled correctly.
  • ContainerDetails correctly passes enableBillingWarning: false to preserve its existing inline-error flow for 402 responses.
  • The DeviceDetails migration from the standalone ConfirmDialog (for accepted-device delete) to the unified DeviceActionDialog portal is safe — DeviceActionDialog fully handles the remove action.
  • No any types, no missing hook dependencies, no stale closures, no security concerns.
  • No cross-repo impact in cloud/ — no references to removed interfaces or renamed components.
  • Tests are thorough and deterministic: core hook behavior, portal wiring, cancel-race scenarios, ref freshness, and billing-warning gating are all covered.

If you've addressed the feedback and want a new review, tag @shellhub-io/admin and a team member can trigger it.


Extract the shared action-dialog state machine into useActionDialogState,
with thin useDeviceActions/useContainerActions wrappers and the
Device/Container ActionsPortal components, replacing the duplicated action
logic in the device and container list/detail pages.

Tests for the hook and portals are rewritten to drive behavior through the
public API (renderHook + userEvent) rather than inspecting passed props.
@luannmoreira luannmoreira force-pushed the feat/use-device-actions branch from 3ba8d27 to 803ea17 Compare June 30, 2026 14:54
@gustavosbarreto gustavosbarreto merged commit 0ecbaf6 into master Jun 30, 2026
13 checks passed
@gustavosbarreto gustavosbarreto deleted the feat/use-device-actions branch June 30, 2026 17:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants