[CSM Portal] Add dashboard builder UI - #1429
Conversation
Lets an admin create or edit a dashboard: add/remove widgets and
sections, configure each widget's filters/shape/params through a
condition editor, and preview a widget's real data before saving by
reusing the same DashboardWidgetTile the live dashboard renders with.
Drafts persist to localStorage only; there is no backend change, so a
drift banner flags when a draft has diverged from what GET
/dashboards/{id} currently serves. Access is frontend-gated to the
admin role via a route guard, since there is no privileged backend
endpoint behind this feature to enforce anything server-side.
Extracts DashboardWidgetGrid out of AgentsLandingPagePilot so the
builder can overlay edit/remove affordances on the same grid the live
dashboard uses, instead of forking a separate renderer.
- widgetQueryConditions: restrict operators to eq/in for non-case
resourceTypes (no other op has a proven query shape anywhere in this
app), and type-recover boolean/numeric scalar values on write instead
of always stringifying them.
- WidgetEditorDialog: thread selectedTeamGroupId/selectedTeamLabel into
the Preview tile so a team-scoped widget previews the same way it
renders on the live dashboard; guard the Row limit field against
NaN/negative/decimal input the way Grid width already is.
- CsmDashboardBuilderEditorPage: gate the drift banner on the live
GET /dashboards/{id} fetch actually resolving (was flashing "not yet
deployed" on every open while the fetch was in flight), add a distinct
notice for a failed fetch, resolve real team context for previewing,
and stop the autosave effect from re-stamping updatedAt on an
unmodified draft's first load.
- DashboardWidgetGrid: pass the section's raw (unresolved) key to
renderSectionActions alongside the display-resolved title, so editing
or removing a placeholder-named section (e.g. "{{currentTeam}}
Escalations") no longer splits it into two sections.
- Remove the unused useSaveDashboardDraft export.
Regression tests added for each fix.
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe web application adds an admin dashboard builder with local drafts, widget editing, previews, access control, and dashboard routes. Administration navigation now uses nested user-management tabs, recursive quick navigation, and legacy redirects. Dashboard rendering is shared through ChangesAdmin dashboard builder and navigation
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Admin
participant Router
participant DashboardBuilderRouteGuard
participant CsmDashboardBuilderListPage
participant CsmDashboardBuilderEditorPage
participant LocalStorage
Admin->>Router: Open dashboard builder
Router->>DashboardBuilderRouteGuard: Check route access
DashboardBuilderRouteGuard->>CsmDashboardBuilderListPage: Render authorized route
CsmDashboardBuilderListPage->>CsmDashboardBuilderEditorPage: Open or create draft
CsmDashboardBuilderEditorPage->>LocalStorage: Load and autosave draft
LocalStorage-->>CsmDashboardBuilderEditorPage: Return draft state
Possibly related PRs
Suggested labels: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
…lumns
Preview no longer caps a list-shape widget to a fixed 420px box (it now
fills the dialog's own content width, matching how DashboardWidgetGrid
already spans a list tile the full row); count/pie/bar previews keep the
compact cap since a big number or small chart shouldn't stretch edge to
edge.
Adds a Columns section to the widget editor for shape "list" (hidden for
count/pie), letting the admin configure the same path/label/format rows
BeDashboardWidget.columns already supports on the wire but that only
DashboardWidgetGrid's real render path exercised until now. buildWidget()
omits `columns` entirely (not `[]`) when no rows are configured, matching
the "absent is a no-op" convention documented on the type and relied on by
DashboardWidgetGrid's own `columns={widget.columns}` passthrough. The
configured columns are also threaded into the Preview's DashboardWidgetTile
call so previewing exercises GenericColumnList for real instead of the
hardcoded per-resourceType fallback.
…r a "User management" tab, sibling to Dashboards
Restructures the Settings ("admin") section's flat 6-tab strip into two levels:
a top-level [User management, Dashboards] strip, with User management owning
its own five sub-tabs. The nav tree (csmNavItems.ts) and its consumers
(useRouteTabs, featureFlags.ts) already modelled an arbitrary-depth tree, so
the extension is mostly additive: useRouteTabs resolves the nested node's
children the same way it resolves the top level, just called again with the
nested id; SectionTabs gains a "secondary" variant for the visually
subordinate nested strip; featureFlags.ts's navigableNavNodes now recurses so
the five directory pages stay reachable from Quick-nav.
Routes move from /admin/<page> to /admin/user-management/<page>; the five old
paths get `<Navigate replace>` redirects so existing deep links/bookmarks
still resolve (a judgment call, flagged for easy revert). The
/admin/<kind>/:id member-detail routes and every `routeBase="/admin/roles"`-
style literal elsewhere in the app are left untouched, since they still work
through the added redirects.
Renames the affected nav-node ids (admin.users -> admin.user-management.users
etc.) — a breaking change to the CSM_PORTAL_FEATURE_OVERRIDES contract,
addressed by updating config.js.example's example/reference; no other
override reference to these ids was found in the repo.
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (3)
apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx (1)
31-46: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAssert the destination pathname for navigation.
Outlet content does not prove the final URL. Add a
useLocation()destination probe. In the layout test, click primary and secondary tabs and assert the pathname. In the redirect test, assert each legacy path resolves to its expected new pathname.
apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx#L31-L46: add a destination probe and interactive tab-navigation assertions.apps/csm-portal/webapp/src/features/csm-admin/pages/adminRoutes.redirects.test.tsx#L41-L84: add a destination probe and assert each redirect target pathname.As per coding guidelines: test actual navigation with
<Routes>and a destination probe usinguseLocation().🤖 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 `@apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx` around lines 31 - 46, Update apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx lines 31-46 by adding a useLocation-based destination probe, then click primary and secondary tabs and assert their resulting pathnames while retaining the existing Routes setup. Update apps/csm-portal/webapp/src/features/csm-admin/pages/adminRoutes.redirects.test.tsx lines 41-84 with the same destination probe and assert that every legacy route resolves to its expected new pathname.Source: Coding guidelines
apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsx (1)
341-365: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThe
max-width: 420pxassertion cannot fail.The dialog sets the cap through the
sxprop. Oxygen UI (MUI) compilessxto a generated CSS class, not an inlinestyleattribute.container.querySelector('[style*="max-width: 420px"]')therefore returnsnullfor both the list shape and the count shape. The test passes even if the regression it guards returns.Assert the resolved style instead, or assert a stable marker the component sets for the full-width case.
♻️ Proposed assertion using the computed style
- // No element in the dialog carries the old fixed-width cap any more — - // a list-shape preview now sizes to the dialog's own content width - // instead. - expect(container.querySelector('[style*="max-width: 420px"]')).toBeNull(); + // No element in the dialog carries the old fixed-width cap any more — + // a list-shape preview now sizes to the dialog's own content width + // instead. `sx` compiles to a class, so read the resolved style rather + // than the inline `style` attribute. + const capped = Array.from(container.querySelectorAll<HTMLElement>("*")).filter( + (el) => window.getComputedStyle(el).maxWidth === "420px", + ); + expect(capped).toHaveLength(0);This test also re-implements
renderDialoginline to reachcontainer. Return the render result fromrenderDialogand reuse it here.🤖 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 `@apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsx` around lines 341 - 365, Update the list-shape preview test to verify the resolved width behavior rather than searching for an inline max-width style, so it fails if the 420px cap returns; preferably assert a stable full-width marker if the component provides one. Refactor renderDialog to return the render result, then reuse its container in this test instead of duplicating the provider setup.apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.test.tsx (1)
164-169: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winThis
waitFordoes not wait for the resolved fetch.
getMockwas already called during the initial render, soexpect(getMock).toHaveBeenCalled()is satisfied on the first tick. The assertions at Lines 168-169 can therefore run before React Query commits the resolved data and before the drift memo recomputes. The test asserts the pre-resolution state a second time rather than the post-resolution state.Wait for an observable effect of the resolution instead.
♻️ Proposed fix to wait for the settled query
resolveGet({ ...draft, sourceDashboardId: undefined }); - await waitFor(() => expect(getMock).toHaveBeenCalled()); - expect(screen.queryByText(/not yet deployed/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/differs from what/i)).not.toBeInTheDocument(); + // The "couldn't check" notice is the only banner that could appear + // once the query settles — assert the settled state directly rather + // than a call count that was already satisfied on mount. + await waitFor(() => { + expect(screen.queryByText(/couldn't check/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/not yet deployed/i)).not.toBeInTheDocument(); + expect(screen.queryByText(/differs from what/i)).not.toBeInTheDocument(); + });🤖 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 `@apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.test.tsx` around lines 164 - 169, Update the test around resolveGet in CsmDashboardBuilderEditorPage so waitFor observes the resolved query state rather than merely confirming the already-invoked getMock. Wait for a post-resolution UI effect or settled query result before asserting that both drift messages are absent.
🤖 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
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.tsx`:
- Around line 189-193: Update handleResourceTypeChange to clear columnDrafts
when the resource type changes, alongside the existing condition and slice-draft
resets. Ensure resource-specific column paths and headers cannot persist across
resource-type switches.
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.tsx`:
- Around line 172-177: Update the autosave effect around saveDashboardDraft and
its cleanup so unmount flushes the latest working value immediately before
clearing the debounce timer. Preserve debounced saves during normal editing and
ensure the cleanup uses the current working state without creating duplicate
saves.
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.tsx`:
- Around line 118-121: Update CsmDashboardBuilderListPage so draftIds.has(d.id)
alone does not render deployment-drift warning styling or text; use the existing
drift comparison to show the warning only for materially changed drafts, or
otherwise present neutral local-draft messaging. In
apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.test.tsx
lines 113-130, seed a materially changed draft for the warning case and add an
exact-match draft case that renders no warning.
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.ts`:
- Around line 68-75: Update isDraft to validate every required DashboardDraft
field, including isDefault, isTeamBased, emptySections, and updatedAt, before
accepting a stored record. In
apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.ts
lines 68-75, preserve existing checks and add type-appropriate validation. In
apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.test.ts
lines 111-115, add a syntactically valid but incomplete draft and verify
listDashboardDrafts ignores it without throwing.
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDrift.ts`:
- Around line 89-91: Update isDraftDrifted to return true when
draft.sourceDashboardId is absent, before checking live or comparing canonical
shapes. Add a dashboardDrift.test.ts case with no sourceDashboardId and a
matching live dashboard, asserting the draft is considered drifted.
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/widgetQueryConditions.test.ts`:
- Around line 152-155: Update queryFromFilterConditions and the preview/save
flows using it so unsupported legacy operators such as notIn are never rewritten
as equality filters. Preserve the original query semantics until the user
selects eq or in, or reject the operation through validation; ensure both
preview and handleSave apply the same behavior. Update the affected test to
verify the chosen behavior.
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/widgetQueryConditions.ts`:
- Around line 183-193: Verify the actual non-case widget query shape against the
search payload types and widget search-call construction before relying on flat
top-level mapping. In the query-to-filter conversion near
queryFromFilterConditions, skip entries whose values are non-scalar and not
arrays of scalars, rather than stringifying objects; preserve scalar and
scalar-array handling, and align the mapping with the confirmed nested or flat
contract so saving a widget cannot overwrite filters with "[object Object]".
- Around line 113-121: Update coerceScalar so numeric strings are converted only
when Number conversion is lossless: reject leading-zero integer representations
and values outside the safe-integer range, and require the numeric value to
round-trip to the original normalized string before returning it. Preserve
boolean coercion and return the original string for identifier-like or otherwise
non-lossless values.
In `@apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx`:
- Around line 25-29: In
apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx#L25-L29
and
apps/csm-portal/webapp/src/features/csm-admin/pages/adminRoutes.redirects.test.tsx#L31-L39,
add top-level mocks for `@api/backend/client` and `@config/apiConfig` before
importing CsmAdminLayout or other transitively dependent modules; ensure the
config mock exports apiConfig.backendUrl as "https://example.test" and apply the
same setup in both test files.
---
Nitpick comments:
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsx`:
- Around line 341-365: Update the list-shape preview test to verify the resolved
width behavior rather than searching for an inline max-width style, so it fails
if the 420px cap returns; preferably assert a stable full-width marker if the
component provides one. Refactor renderDialog to return the render result, then
reuse its container in this test instead of duplicating the provider setup.
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.test.tsx`:
- Around line 164-169: Update the test around resolveGet in
CsmDashboardBuilderEditorPage so waitFor observes the resolved query state
rather than merely confirming the already-invoked getMock. Wait for a
post-resolution UI effect or settled query result before asserting that both
drift messages are absent.
In `@apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx`:
- Around line 31-46: Update
apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx
lines 31-46 by adding a useLocation-based destination probe, then click primary
and secondary tabs and assert their resulting pathnames while retaining the
existing Routes setup. Update
apps/csm-portal/webapp/src/features/csm-admin/pages/adminRoutes.redirects.test.tsx
lines 41-84 with the same destination probe and assert that every legacy route
resolves to its expected new pathname.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: ae49a244-6a0a-4c4d-b566-938fbc5e4499
📒 Files selected for processing (31)
apps/csm-portal/webapp/public/config.js.exampleapps/csm-portal/webapp/src/App.tsxapps/csm-portal/webapp/src/components/section-tabs/SectionTabs.tsxapps/csm-portal/webapp/src/config/csmNavItems.test.tsapps/csm-portal/webapp/src/config/csmNavItems.tsapps/csm-portal/webapp/src/config/featureFlags.test.tsapps/csm-portal/webapp/src/config/featureFlags.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetFilterConditionEditor.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetFilterConditionEditor.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/DashboardBuilderRouteGuard.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/DashboardBuilderRouteGuard.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardBuilderAccess.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardBuilderAccess.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDrift.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDrift.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/widgetQueryConditions.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/widgetQueryConditions.tsapps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsxapps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.tsxapps/csm-portal/webapp/src/features/csm-admin/pages/adminRoutes.redirects.test.tsxapps/csm-portal/webapp/src/features/csm-dashboard/components/AgentsLandingPagePilot.tsxapps/csm-portal/webapp/src/features/csm-dashboard/components/DashboardWidgetGrid.tsxapps/csm-portal/webapp/src/features/csm-dashboard/utils/dashboardWidgetGridLayout.ts
Replace the free-text column path input with a freeSolo autocomplete offering the actual attribute paths discovered by sampling the widget's own Preview response, so an admin picks a real field instead of typing a path blind. Falls back to a plain text field with a helper hint until Preview has been run once, and free text remains accepted so a rare field absent from every sampled row is still reachable. The discovery query reuses useWidgetData with the same arguments the Preview tile passes internally, so it shares that tile's TanStack Query cache entry rather than firing a second network request.
… PR wso2-open-operations#1429 Fixes several correctness/crash bugs flagged in review: - Flush a still-pending autosave on unmount instead of discarding it, so an edit made just before navigating away isn't silently lost (this feature has no backend copy). - Only warn about a local dashboard draft when it actually differs from what's deployed (reusing isDraftDrifted), instead of flagging any draft's mere existence. - Validate every required DashboardDraft field in the localStorage read path's type guard, so an incomplete stored record is dropped instead of crashing the drafts list sort. - isDraftDrifted now reports drift for a draft with no sourceDashboardId even when a live dashboard happens to be passed in, matching its own doc comment. - Non-case widget filter rows with an operator the flat search contract can't express are dropped on save rather than silently reinterpreted as eq, which used to flip a legacy notIn filter's real meaning. - coerceScalar only converts a numeric-looking string when it round-trips back to the exact same text, so a leading-zero identifier or a value above Number.MAX_SAFE_INTEGER is kept as a string instead of corrupted. - Clarified the widgetQueryConditions.ts doc comment: a widget's own query is flat and maps onto the search payload's inner filters object, not a second nested filters key. - Added the @api/backend/client / @config/apiConfig mocks to CsmAdminLayout.test.tsx and adminRoutes.redirects.test.tsx per this repo's existing convention for anything transitively importing CsmAdminLayout.
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.test.ts (1)
75-78: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMake the ordering test deterministic.
setTimeout(2)does not guarantee that the two calls tosaveDashboardDraftreceive differentupdatedAtvalues. If both values are equal, the sort can preserve insertion order and the assertion can fail even when the implementation is correct. Use a controlled clock or a test-only timestamp seam instead of a wall-clock delay.🤖 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 `@apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.test.ts` around lines 75 - 78, Make the “lists drafts most-recently-updated first” test deterministic by replacing the setTimeout delay with a controlled clock or test-only timestamp seam around saveDashboardDraft. Ensure the older draft receives an earlier updatedAt than the newer draft, while preserving the existing ordering assertion.
🤖 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
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.tsx`:
- Around line 196-201: Update handleResourceTypeChange to clear previewSnapshot
alongside the existing resource type, condition, slice, and column state resets.
Ensure switching resource types requires a new preview before useWidgetData
derives discoveredColumnPaths for the new resource type.
---
Outside diff comments:
In
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.test.ts`:
- Around line 75-78: Make the “lists drafts most-recently-updated first” test
deterministic by replacing the setTimeout delay with a controlled clock or
test-only timestamp seam around saveDashboardDraft. Ensure the older draft
receives an earlier updatedAt than the newer draft, while preserving the
existing ordering assertion.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 9c573d87-842b-40e4-9d3b-a0a46fe4bdf7
📒 Files selected for processing (16)
apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDrift.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDrift.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/discoverAttributePaths.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/discoverAttributePaths.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/widgetQueryConditions.test.tsapps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/widgetQueryConditions.tsapps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsxapps/csm-portal/webapp/src/features/csm-admin/pages/adminRoutes.redirects.test.tsx
🚧 Files skipped from review as they are similar to previous changes (11)
- apps/csm-portal/webapp/src/features/csm-admin/pages/CsmAdminLayout.test.tsx
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDrift.ts
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.tsx
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDrift.test.ts
- apps/csm-portal/webapp/src/features/csm-admin/pages/adminRoutes.redirects.test.tsx
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/widgetQueryConditions.test.ts
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.test.tsx
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderEditorPage.tsx
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsx
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/utils/dashboardDraftsStorage.ts
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/pages/CsmDashboardBuilderListPage.test.tsx
…ype change Switching resourceType left previewSnapshot pointing at the old resource type's preview data, so discoveredColumnPaths kept offering paths (e.g. project.key) that don't resolve under the new resourceType. Clear previewSnapshot alongside the existing conditions/slices/columns resets in handleResourceTypeChange, requiring a fresh Preview before any column paths are offered again. Addresses CodeRabbit finding on PR wso2-open-operations#1429 (discussion_r3761150228).
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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
`@apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsx`:
- Around line 494-500: Update the resourceType-switch test around the combobox
interaction to wrap the postMock no-refetch assertion in await waitFor. Keep the
existing expectation that switching to “incident” does not trigger a Preview
request, while allowing promise-based TanStack Query work to settle before
asserting.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 9503388d-e64d-4f5b-9528-1530722794ac
📒 Files selected for processing (2)
apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.test.tsxapps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/csm-portal/webapp/src/features/csm-admin/dashboards/components/WidgetEditorDialog.tsx
…n resourceType change `fireEvent` only flushes the synchronous React commit, not TanStack Query's microtask-scheduled fetch machinery, so the prior bare synchronous `expect(postMock).not.toHaveBeenCalled()` right after the resourceType switch could pass even if a regression reintroduced a delayed refetch. A bare `await waitFor(() => expect(postMock).not.toHaveBeenCalled())` isn't a fix either: waitFor only retries while the assertion keeps failing, so an already-true negative assertion resolves on the first check and never gives a delayed call a chance to land. Instead, force a real macrotask turn (act-wrapped, to avoid an unwrapped-update warning) before asserting, which fully drains any pending microtask-queued query work ahead of it. Verified this actually catches a reintroduced delayed-refetch regression before reverting the probe.
|
@coderabbitai resume |
✅ Action performedReviews resumed. |
Purpose
We are adding an admin-only dashboard builder to the CSM portal so dashboards no longer require a hand-authored config file round-trip to preview or iterate on. Today dashboard layout/widget config is static and not editable from the app at all; this adds the missing authoring surface.
Goals
/admin/user-management's sibling "Dashboards" tab.BeDashboardWidget.columns/GenericColumnListrendering path that existed but was previously unreachable from any UI./admin/{users,roles,groups,teams,permissions}paths redirect to their new/admin/user-management/*locations.Approach
New
features/csm-admin/dashboards/module: a list page, an editor page, a widget-editor dialog with a filter-condition editor, and alocalStorage-backed draft store (no backend change — dashboard config is not runtime-editable server-side yet, so this is intentionally scoped to the authoring/preview layer only). Access to the new routes is frontend-gated to theadminrole via a route guard (the existing/adminsection has no backend authorization to fall back on either, so this matches the section's current posture rather than introducing a new gap).The dashboard-render grid was extracted from
AgentsLandingPagePilotinto a reusableDashboardWidgetGridcomponent so the builder overlays edit/remove affordances on the exact same grid the live dashboard renders with, instead of forking a second renderer. The widget editor's "Preview" button renders the in-progress widget config through the realDashboardWidgetTile, so preview data is genuine, not mocked.Grown across three follow-up commits (bug fixes from a pre-PR review pass, column config + preview-width, and the Settings nav regroup): 31 files, 4 over CodeRabbit's ~300-line line-by-line threshold (
WidgetEditorDialog.tsx/.test.tsx,CsmDashboardBuilderEditorPage.tsx/.test.tsx) — all core editor surfaces for one cohesive feature; didn't split cleanly without leaving partial/broken functionality across PRs. Expect summary-level review on those four.The Settings nav change also moves 5 existing routes to a new
/admin/user-management/*prefix, with<Navigate replace>redirects from the old paths so no existing bookmark/deep-link breaks.Known limitation: the filter-condition editor only offers
eq/inoperators for non-caseresource types (incidents, accounts, etc.). Those endpoints' search payloads are bespoke, flat, named-key shapes with no provennotIn/gte/lte/isEmptyconvention anywhere in this codebase today, so rather than invent one, the editor restricts to what's actually provable.case(and its type variants) keep the full operator set via the existing generic filter DSL. Widening this needs the real query convention confirmed against the backend first.User stories
As an admin, I can build and preview a CSM dashboard's layout and widgets without hand-editing a config file, and I can tell at a glance whether my draft matches what's actually live.
Release note
Added an admin-only dashboard builder under Admin > Dashboards, with per-widget filter configuration and live preview.
Documentation
N/A — internal admin tooling, no end-user-facing product documentation impact.
Training
N/A — no training content affected.
Certification
N/A — no certification exam impact.
Marketing
N/A — internal admin tooling, not a marketed feature.
Automation tests
Security checks
eslintandtsc -bran clean instead.Samples
N/A — no new sample apps or code samples included.
Related PRs
None.
Migrations (if applicable)
N/A — no data or schema migrations; storage is client-side
localStorageonly.Test environment
Node.js webapp toolchain (Vite/vitest/tsc/eslint) run locally in a git worktree; no browser/OS/DB matrix applicable — no backend or database involved in this change.
Learning
N/A.
Summary by CodeRabbit