Skip to content

feat(aurora): security group data grid, permissions#952

Draft
KirylSAP wants to merge 5 commits into
mainfrom
kiryl-security-group-refactor
Draft

feat(aurora): security group data grid, permissions#952
KirylSAP wants to merge 5 commits into
mainfrom
kiryl-security-group-refactor

Conversation

@KirylSAP

@KirylSAP KirylSAP commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixed permission-related bugs where users without appropriate permissions could see and attempt actions, and where error messages persisted when closing and reopening modals.

Bugs Fixed

Bug 1: Edit on the DataGrid and details of Security group page

Bug 2: Error message persist on the Create security group Modal

Task: Implement DataGrid Header for Security Groups

Changes Made

Core Changes

  • Created reusable hook useSecurityGroupPermissions that fetches all 9 security group permissions with proper React Query configuration (infinite cache)
  • Updated SecurityGroupsList to use the new hook and hide Create button when canCreate is false
  • Updated SecurityGroupDetails to use the new hook for consistent permission handling
  • Removed unused props from SecurityGroups component (removed client prop)

DataGrid Refactor

  • Implemented new three-zone DataGrid toolbar following the pattern from Images list:
    • Zone 1: Sort controls and Create button (permission-gated)
    • Zone 2: Filter input with search, active filter pills
    • Zone 3: Bulk actions toolbar (only visible when user has permissions)
  • Added filtering and search functionality: Users can filter by "shared" status and search by name/description
  • Added URL state management: Filters, search, and sort state are persisted in URL query parameters via urlHelpers.ts
  • Debounced search: Search input commits to URL with 500ms debounce to avoid excessive navigation updates

Permission-Based UI Updates

All UI elements now properly respect user permissions:

  • SecurityGroupsList: Create button only visible when canCreate is true
  • SecurityGroupTableRow: Edit and Delete menu items only visible when respective permissions are true
  • SecurityGroupRulesTable: Add Rule button and Actions column only visible when canCreateRule / canDeleteRule are true
  • SecurityGroupRBACPolicies: Share Security Group button only visible when canManageAccess is true
  • RBACPolicyRow: Delete action only visible when canDelete is true
  • SecurityGroupBasicInfo: Edit button only visible when canUpdate is true

Modal State Management

  • CreateSecurityGroupModal: Now resets error state in handleClose() to prevent error persistence
  • EditSecurityGroupModal: Now resets error state in handleClose() to prevent error persistence

Test Updates

  • Updated all test files to include missing permission fields:
    • SecurityGroupListContainer.test.tsx
    • SecurityGroupTableRow.test.tsx
    • SecurityGroupRulesTable.test.tsx
    • SecurityGroupRBACPolicies.test.tsx
    • SecurityGroupDetailsView.test.tsx

Related Issues

Fixes permission visibility bugs where users could see and attempt actions they didn't have permissions for, and where error messages persisted across modal reopens.

Key Technical Details

Permission Structure

All 9 permissions are now consistently checked:

  • network:security_groups:readcanView
  • network:security_groups:createcanCreate
  • network:security_groups:updatecanUpdate
  • network:security_groups:deletecanDelete
  • network:security_group_rules:createcanCreateRule
  • network:security_group_rules:deletecanDeleteRule
  • network:rbac_policies:create + network:rbac_policies:deletecanManageAccess
  • network:rbac_policies:readcanViewRBAC

React Query Configuration

The hook uses:

  • staleTime: Infinity - permissions don't change during session
  • gcTime: Infinity - keep in cache forever
  • enabled: Boolean(projectId) - only fetch if projectId exists

Why Remove Stateful Field from Edit?

OpenStack Neutron distinguishes between:

  • create_security_group - allows setting stateful during creation
  • update_security_group:stateful - requires cloud admin role to modify after creation

Regular users can set stateful during creation but cannot change it later, so the field was removed from the Edit modal.

Testing Instructions

  1. pnpm i
  2. pnpm run typecheck - verify no TypeScript errors
  3. pnpm run test - verify all tests pass
  4. Test in UI with different permission levels:
    • User with full permissions should see all buttons/actions
    • User with read-only permissions should only see view/details actions
    • User without rule permissions should not see Add Rule button or delete actions on rules
    • User without RBAC permissions should not see Share Security Group button

Checklist

  • I have performed a self-review of my code.
  • I have commented my code, particularly in hard-to-understand areas.
  • I have added tests that prove my fix is effective or that my feature works.
  • New and existing unit tests pass locally with my changes.
  • I have made corresponding changes to the documentation (if applicable).
  • My changes generate no new warnings or errors.

Summary by CodeRabbit

  • New Features

    • Security groups now respect user permissions for viewing, creating, updating, deleting, and managing access.
    • Added search, sort, and filter controls directly in security group and rules views, with clearer result counts.
    • Introduced selection support in list rows for bulk actions.
    • Added clearer help text and permission-related messaging in creation and edit flows.
  • Bug Fixes

    • Action buttons and menus now only appear when the user has the required access.
    • Search behavior is now more responsive and consistent across security group screens.

@KirylSAP KirylSAP self-assigned this Jun 22, 2026
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

Introduces a useSecurityGroupPermissions hook and threads explicit permission flags (canCreate, canUpdate, canDelete, canCreateRule, canDeleteRule, canManageAccess, canViewRBAC) through security group detail, rules, RBAC, and list components, replacing prior readOnly/ownership-based gating. Refactors search UIs to debounced inputs, adds URL-driven filtering/sorting, bulk-selection scaffolding, removes editable "stateful" attribute from edit modal, and updates locale strings.

Changes

Security Group Permissions Rollout

Layer / File(s) Summary
Permissions hook
.../-hooks/useSecurityGroupPermissions.ts
New SecurityGroupPermissions interface and hook fetching boolean permission flags via a TRPC canUser query with infinite caching.
Details route wiring and BasicInfo gating
$securityGroupId/index.tsx, .../SecurityGroupDetailsView.tsx, .../SecurityGroupDetailsView.test.tsx, .../RBACPolicyRow.tsx, .../SecurityGroupBasicInfo.tsx
Route uses the permissions hook with a loading screen and safe fallback; SecurityGroupDetailsView gates RBAC tab and edit button via canViewRBAC/canUpdate; RBACPolicyRow and SecurityGroupBasicInfo accept canDelete/canUpdate props.
Rules table permission gating
.../SecurityGroupRulesTable.tsx, .../SecurityGroupRulesTable.test.tsx
Replaces readOnly with canCreateRule/canDeleteRule; refactors toolbar to DataGridToolbar with debounced search, sort, filters, gating Add rule/Actions/dialogs/modal.
RBAC policies permission gating
.../SecurityGroupRBACPolicies.tsx, .../SecurityGroupRBACPolicies.test.tsx
Adds canManageAccess, gates Share button and row deletion, refactors search to debounced DataGridToolbar; tests updated with new placeholder and debounce waits.
List container bulk-selection
.../SecurityGroupTableRow.tsx, .../SecurityGroupTableRow.test.tsx, .../SecurityGroupListContainer.tsx, .../SecurityGroupListContainer.test.tsx
Expands SecurityGroupPermissions, adds optional row-selection props, bulk-action column, promise-based onUpdateSecurityGroup, and onClearUpdateError.
URL-driven list filtering
.../urlHelpers.ts, .../SecurityGroupsList.tsx, securitygroups/index.tsx, removed SecurityGroupsList.test.tsx
Adds URL helper functions for filters/sort/search; refactors list to derive state from URL params, use permissions hook, and navigate on changes.
Edit modal stateful removal
.../EditSecurityGroupModal.tsx, .../CreateSecurityGroupModal.tsx
Removes editable stateful field from edit flow, replaces with a permission note; adds helptext to create modal's Stateful checkbox.
Locale updates
packages/aurora/src/locales/{de,en}/messages.po
Adds/removes strings for permission loading, search placeholders, count labels, stateful description, and access-denied messaging.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Route as SecurityGroupId Route
  participant Hook as useSecurityGroupPermissions
  participant DetailsView as SecurityGroupDetailsView
  participant RulesTable as SecurityGroupRulesTable
  participant RBACPolicies as SecurityGroupRBACPolicies

  Route->>Hook: useSecurityGroupPermissions(projectId)
  Hook-->>Route: permissions / isLoading / isError
  Route->>DetailsView: permissions (or safePermissions)
  DetailsView->>RulesTable: canCreateRule, canDeleteRule
  DetailsView->>RBACPolicies: canManageAccess
  RulesTable-->>DetailsView: gated Add rule / delete actions
  RBACPolicies-->>DetailsView: gated Share / remove actions
Loading
sequenceDiagram
  participant User
  participant SecurityGroupsList
  participant urlHelpers
  participant Router

  User->>SecurityGroupsList: change filter/sort/search
  SecurityGroupsList->>urlHelpers: applyFilterSelection/buildUrlSearchParams
  urlHelpers-->>SecurityGroupsList: updated params
  SecurityGroupsList->>Router: navigate({ search, replace: true })
  Router-->>SecurityGroupsList: updated searchParams
Loading

Possibly related issues

Possibly related PRs

Suggested labels: aurora-portal, enhancement, ux improvement

Suggested reviewers: vlad-schur-external-sap, andypf, TilmanHaupt

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main changes: security group data grid and permission handling.
Description check ✅ Passed The description covers the summary, changes made, related issues, testing instructions, checklist, and key implementation details.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch kiryl-security-group-refactor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@KirylSAP KirylSAP marked this pull request as ready for review July 6, 2026 08:10
@KirylSAP KirylSAP requested a review from a team as a code owner July 6, 2026 08:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRBACPolicies.test.tsx (1)

1-632: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

No test coverage for canManageAccess={false}.

Every render call in this file passes canManageAccess={true}. This leaves the permission-gating logic — hiding the "Share Security Group" button and disabling delete on RBACPolicyRow via canDelete={canManageAccess} — completely untested for the restricted case. Note also that RBACPolicyRow is mocked here (lines 40-48) and always renders its Delete button regardless of canDelete, so even a canManageAccess={false} test using the current mock wouldn't verify the button is hidden — the mock would need to respect canDelete too for such a test to be meaningful.

Consider adding a test with canManageAccess={false} asserting the Share button is absent, plus updating the RBACPolicyRow mock to honor canDelete.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRBACPolicies.test.tsx
around lines 1 - 632, Add coverage for the restricted permission path in
SecurityGroupRBACPolicies by introducing a test that renders with
canManageAccess=false and asserts the Share Security Group action is hidden.
Update the RBACPolicyRow mock to respect the canDelete prop so it only renders
the Delete button when deletion is allowed, otherwise the test will not verify
the real gating behavior. Use the existing SecurityGroupRBACPolicies render
setup and RBACPolicyRow mock symbol to keep the new case aligned with the
current test structure.
packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRulesTable.test.tsx (1)

95-618: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

No test coverage for canCreateRule={false} / canDeleteRule={false}.

Every render call across this file passes canCreateRule={true} and canDeleteRule={true}. Since gating the Add rule button, Actions column, delete menu, delete dialog, and add-rule modal on these exact flags is the entire purpose of this change, there is currently no test that would catch a regression such as an inverted condition (e.g. !canDeleteRule) or an accidentally-always-true gate.

Consider adding at least two tests:

  • canCreateRule={false} → "Add rule" button is not rendered even when onCreateRule/securityGroupId are provided.
  • canDeleteRule={false} → "Actions" header, per-row delete menu, and DeleteRuleDialog are not rendered.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRulesTable.test.tsx
around lines 95 - 618, The SecurityGroupRulesTable test suite is missing
coverage for the permission gates, so add assertions for the canCreateRule and
canDeleteRule branches in SecurityGroupRulesTable. Introduce a test that renders
with canCreateRule set to false while still providing onCreateRule and
securityGroupId, and verify the Add rule button is hidden. Also add a test that
renders with canDeleteRule set to false and verify the Actions header and
delete-related UI (row delete menu / DeleteRuleDialog trigger) are not shown, so
regressions in the gating logic are caught.
🧹 Nitpick comments (1)
packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/EditSecurityGroupModal.tsx (1)

148-149: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

No user-visible explanation for the missing Stateful field.

The prior informational message about the stateful restriction was removed and replaced only with a JSX comment ({/* Note: ... */}), which is never rendered. Users editing the security group now see no indication of why the Stateful checkbox disappeared, unlike before. This is also inconsistent with the summary's description of "an inline note about elevated permission" — there is no visible note in the actual UI.

Consider rendering a brief Message/help text in the form instead of a code-only comment.

💡 Example: surface a visible note
             </FormRow>
+            <FormRow className="mb-0">
+              <Message variant="info" dismissible={false}>
+                <Trans>Stateful cannot be changed here. Updating it requires elevated permissions.</Trans>
+              </Message>
+            </FormRow>
-            {/* Note: Stateful checkbox is not shown here because it requires 'update_security_group:stateful' permission (cloud admin only) */}

Also applies to: 187-187

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/-components/-modals/EditSecurityGroupModal.tsx
around lines 148 - 149, The Stateful restriction note is currently only present
as a JSX comment in EditSecurityGroupModal and never reaches the UI, so users
get no explanation for the missing field. Update EditSecurityGroupModal to
render a visible inline help/message element near the stateful checkbox area
(and keep it consistent with the elevated-permission summary) instead of relying
on the comment-only placeholder, so the restriction is clearly shown when
editing a security group.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/-components/SecurityGroupsList.tsx:
- Around line 306-312: The CreateSecurityGroupModal usage in SecurityGroupsList
is not wired to the mutation state and leaves stale errors behind. Update the
create flow so the modal’s loading prop uses the createSecurityGroupMutation
pending state instead of a hardcoded false, and make the close handler clear
createError before or when setting createModalOpen to false, matching the
behavior used for the Edit modal in SecurityGroupListContainer.
- Around line 40-72: The toolbar state in SecurityGroupsList is only initialized
from searchParams once, so sortSettings, filterSettings, and searchTerm can get
out of sync when the URL changes. Update SecurityGroupsList to derive these
controls directly from searchParams or add an effect that resyncs the local
state whenever searchParams changes, using the existing parseFiltersFromUrl,
sortSettings, filterSettings, and searchTerm state as the key places to adjust.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRulesTable.tsx:
- Around line 66-75: `localSearchTerm` in `SecurityGroupRulesTable` is only
initialized from the `searchTerm` prop once, so it can drift from later parent
updates. Add an effect to keep `localSearchTerm` in sync whenever `searchTerm`
changes, and make sure the search input state reflects external resets like
clear-filters or URL-driven updates. Use the existing `useState`, `useEffect`,
and `debounceTimer` setup in `SecurityGroupRulesTable` to locate the change.

---

Outside diff comments:
In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRBACPolicies.test.tsx:
- Around line 1-632: Add coverage for the restricted permission path in
SecurityGroupRBACPolicies by introducing a test that renders with
canManageAccess=false and asserts the Share Security Group action is hidden.
Update the RBACPolicyRow mock to respect the canDelete prop so it only renders
the Delete button when deletion is allowed, otherwise the test will not verify
the real gating behavior. Use the existing SecurityGroupRBACPolicies render
setup and RBACPolicyRow mock symbol to keep the new case aligned with the
current test structure.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRulesTable.test.tsx:
- Around line 95-618: The SecurityGroupRulesTable test suite is missing coverage
for the permission gates, so add assertions for the canCreateRule and
canDeleteRule branches in SecurityGroupRulesTable. Introduce a test that renders
with canCreateRule set to false while still providing onCreateRule and
securityGroupId, and verify the Add rule button is hidden. Also add a test that
renders with canDeleteRule set to false and verify the Actions header and
delete-related UI (row delete menu / DeleteRuleDialog trigger) are not shown, so
regressions in the gating logic are caught.

---

Nitpick comments:
In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/-components/-modals/EditSecurityGroupModal.tsx:
- Around line 148-149: The Stateful restriction note is currently only present
as a JSX comment in EditSecurityGroupModal and never reaches the UI, so users
get no explanation for the missing field. Update EditSecurityGroupModal to
render a visible inline help/message element near the stateful checkbox area
(and keep it consistent with the elevated-permission summary) instead of relying
on the comment-only placeholder, so the restriction is clearly shown when
editing a security group.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5e491a1a-6e78-4ab6-be0b-938d9672d9a5

📥 Commits

Reviewing files that changed from the base of the PR and between fd1a2ba and c144e7a.

📒 Files selected for processing (24)
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/RBACPolicyRow.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupBasicInfo.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRBACPolicies.test.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRBACPolicies.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRulesTable.test.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRulesTable.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/SecurityGroupDetailsView.test.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/-components/SecurityGroupDetailsView.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/$securityGroupId/index.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/CreateSecurityGroupModal.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/-modals/EditSecurityGroupModal.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupListContainer.test.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupListContainer.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupTableRow.test.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupTableRow.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupsList.test.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupsList.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-hooks/useSecurityGroupPermissions.ts
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/index.tsx
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/urlHelpers.ts
  • packages/aurora/src/locales/de/messages.po
  • packages/aurora/src/locales/de/messages.ts
  • packages/aurora/src/locales/en/messages.po
  • packages/aurora/src/locales/en/messages.ts
💤 Files with no reviewable changes (1)
  • packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupsList.test.tsx

Comment on lines +40 to +72
const [sortSettings, setSortSettings] = useState<RequiredSortSettings>({
options: [
{ label: t`Name`, value: "name" },
{ label: t`Project id`, value: "project_id" },
],
sortBy: searchParams.sortBy || "name",
sortDirection: searchParams.sortDirection || "asc",
})

const [filterSettings, setFilterSettings] = useState<FilterSettings>({
filters: [
{
displayName: t`Shared`,
filterName: "shared",
values: ["true", "false"],
supportsMultiValue: false,
},
],
selectedFilters: parseFiltersFromUrl(searchParams),
})

const [searchTerm, setSearchTerm] = useState(searchParams.search || "")
const [createModalOpen, setCreateModalOpen] = useState(false)
const [deleteError, setDeleteError] = useState<string | null>(null)
const [createError, setCreateError] = useState<string | null>(null)
const [updateError, setUpdateError] = useState<string | null>(null)

const { searchTerm, sortSettings, filterSettings, handleSearchChange, handleSortChange, handleFilterChange } =
useListWithFiltering<SecurityGroupSortKey>({
defaultSortKey: "name",
defaultSortDir: "asc",
sortOptions: [
{ label: t`Name`, value: "name" },
{ label: t`Project id`, value: "project_id" },
],
filterSettings: {
filters: [
{
displayName: t`Shared`,
filterName: "shared",
values: Object.values(SECURITY_GROUP_SHARED),
supportsMultiValue: false,
},
],
},
})

const utils = trpcReact.useUtils()

// TODO: replace with trpc.network.canUser when security group permissions are available
const permissions = {
canCreate: true,
canUpdate: true,
canDelete: true,
canManageAccess: true,
}
const urlFilters = parseFiltersFromUrl(searchParams)
const urlSortBy = (searchParams.sortBy || "name") as SecurityGroupSortKey
const urlSortDirection = searchParams.sortDirection || "asc"
const urlSearchTerm = searchParams.search || ""

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the component and related route definitions.
git ls-files 'packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/**' \
  'packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/**/*' \
  'packages/aurora/src/client/routes/_auth/projects/$projectId/network/**' \
  | sed 's#^`#FILE` #'

echo
echo "=== outline: SecurityGroupsList.tsx ==="
ast-grep outline 'packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupsList.tsx' --view expanded

echo
echo "=== outline: route files nearby ==="
fd -a 'route.tsx|route.ts|_auth.*securitygroups|securitygroups' packages/aurora/src/client/routes | sed -n '1,120p'

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 12207


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the relevant component slices and nearby route integration.
FILE='packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/-components/SecurityGroupsList.tsx'

echo "=== file line count ==="
wc -l "$FILE"

echo
echo "=== SecurityGroupsList.tsx (selected lines) ==="
sed -n '1,360p' "$FILE" | cat -n | sed -n '1,360p'

echo
echo "=== search for searchParams sync / useEffect / router changes ==="
rg -n 'useEffect|searchParams|setSortSettings|setFilterSettings|setSearchTerm|navigate\(|router|replace\(' "$FILE"

echo
echo "=== sibling route/layout files ==="
fd -a 'route.tsx|route.ts' 'packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups' | sed -n '1,120p'

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 14863


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for FILE in \
  'packages/aurora/src/client/routes/_auth/projects/$projectId/network/securitygroups/index.tsx' \
  'packages/aurora/src/client/routes/_auth/projects/$projectId/network/index.tsx'
do
  echo "=== $FILE ==="
  wc -l "$FILE"
  sed -n '1,220p' "$FILE" | cat -n
  echo
done

Repository: cobaltcore-dev/aurora-dashboard

Length of output: 2239


Keep the toolbar state in sync with the URL. sortSettings, filterSettings, and searchTerm are initialized from searchParams once, but the list query reads fresh searchParams on every render. When the URL changes via back/forward navigation or another in-place update, the data changes while the sort/filter/search controls stay stale. Derive the controls from searchParams or resync the local state in an effect.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/-components/SecurityGroupsList.tsx
around lines 40 - 72, The toolbar state in SecurityGroupsList is only
initialized from searchParams once, so sortSettings, filterSettings, and
searchTerm can get out of sync when the URL changes. Update SecurityGroupsList
to derive these controls directly from searchParams or add an effect that
resyncs the local state whenever searchParams changes, using the existing
parseFiltersFromUrl, sortSettings, filterSettings, and searchTerm state as the
key places to adjust.

Comment on lines 306 to 312
<CreateSecurityGroupModal
isOpen={createModalOpen}
onClose={() => setCreateModalOpen(false)}
onCreate={handleCreateSecurityGroup}
isLoading={createSecurityGroupMutation.isPending}
isLoading={false}
error={createError}
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Create modal loading indicator hardcoded and error not cleared on close.

isLoading={false} ignores createSecurityGroupMutation.isPending, so no spinner shows while creating. Also onClose only closes the modal without clearing createError, so reopening after a failed create re-shows the stale error — the same class of bug flagged for the Edit modal in SecurityGroupListContainer.tsx.

🐛 Proposed fix
       <CreateSecurityGroupModal
         isOpen={createModalOpen}
-        onClose={() => setCreateModalOpen(false)}
+        onClose={() => {
+          setCreateModalOpen(false)
+          setCreateError(null)
+        }}
         onCreate={handleCreateSecurityGroup}
-        isLoading={false}
+        isLoading={createSecurityGroupMutation.isPending}
         error={createError}
       />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<CreateSecurityGroupModal
isOpen={createModalOpen}
onClose={() => setCreateModalOpen(false)}
onCreate={handleCreateSecurityGroup}
isLoading={createSecurityGroupMutation.isPending}
isLoading={false}
error={createError}
/>
<CreateSecurityGroupModal
isOpen={createModalOpen}
onClose={() => {
setCreateModalOpen(false)
setCreateError(null)
}}
onCreate={handleCreateSecurityGroup}
isLoading={createSecurityGroupMutation.isPending}
error={createError}
/>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/-components/SecurityGroupsList.tsx
around lines 306 - 312, The CreateSecurityGroupModal usage in SecurityGroupsList
is not wired to the mutation state and leaves stale errors behind. Update the
create flow so the modal’s loading prop uses the createSecurityGroupMutation
pending state instead of a hardcoded false, and make the close handler clear
createError before or when setting createModalOpen to false, matching the
behavior used for the Edit modal in SecurityGroupListContainer.

Comment on lines +66 to +75
canCreateRule,
canDeleteRule,
}: SecurityGroupRulesTableProps) {
const { t } = useLingui()
const [ruleToDelete, setRuleToDelete] = useState<SecurityGroupRule | null>(null)
const [isAddRuleModalOpen, toggleAddRuleModal] = useModal()
const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm)
const debounceTimer = useRef<number | undefined>(undefined)

useEffect(() => () => clearTimeout(debounceTimer.current), [])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

localSearchTerm never re-syncs with an externally-changed searchTerm prop.

localSearchTerm is initialized from searchTerm only once via useState(searchTerm). If the parent resets/changes searchTerm later (e.g. a "clear filters" action, tab switch, or URL-driven state reset per the PR's URL-persistence feature), the input will keep showing the stale local value while the actual applied search term silently differs.

🔧 Proposed fix
   const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm)
   const debounceTimer = useRef<number | undefined>(undefined)

   useEffect(() => () => clearTimeout(debounceTimer.current), [])
+
+  useEffect(() => {
+    setLocalSearchTerm(searchTerm)
+  }, [searchTerm])
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
canCreateRule,
canDeleteRule,
}: SecurityGroupRulesTableProps) {
const { t } = useLingui()
const [ruleToDelete, setRuleToDelete] = useState<SecurityGroupRule | null>(null)
const [isAddRuleModalOpen, toggleAddRuleModal] = useModal()
const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm)
const debounceTimer = useRef<number | undefined>(undefined)
useEffect(() => () => clearTimeout(debounceTimer.current), [])
canCreateRule,
canDeleteRule,
}: SecurityGroupRulesTableProps) {
const { t } = useLingui()
const [ruleToDelete, setRuleToDelete] = useState<SecurityGroupRule | null>(null)
const [isAddRuleModalOpen, toggleAddRuleModal] = useModal()
const [localSearchTerm, setLocalSearchTerm] = useState(searchTerm)
const debounceTimer = useRef<number | undefined>(undefined)
useEffect(() => () => clearTimeout(debounceTimer.current), [])
useEffect(() => {
setLocalSearchTerm(searchTerm)
}, [searchTerm])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/aurora/src/client/routes/_auth/projects/`$projectId/network/securitygroups/$securityGroupId/-components/-details/SecurityGroupRulesTable.tsx
around lines 66 - 75, `localSearchTerm` in `SecurityGroupRulesTable` is only
initialized from the `searchTerm` prop once, so it can drift from later parent
updates. Add an effect to keep `localSearchTerm` in sync whenever `searchTerm`
changes, and make sure the search input state reflects external resets like
clear-filters or URL-driven updates. Use the existing `useState`, `useEffect`,
and `debounceTimer` setup in `SecurityGroupRulesTable` to locate the change.

@KirylSAP KirylSAP marked this pull request as draft July 6, 2026 08:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant