Skip to content

improve: widgets — orphaned widget handling, duplicate template, test query - #296

Merged
alfredo1996 merged 11 commits into
release/1.0from
improve/widgets-integration
Mar 31, 2026
Merged

improve: widgets — orphaned widget handling, duplicate template, test query#296
alfredo1996 merged 11 commits into
release/1.0from
improve/widgets-integration

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented Mar 31, 2026

Copy link
Copy Markdown
Owner

Summary

Integration branch combining widget PRs:

Closes #270, #271

Test plan

  • CI: all checks pass
  • SonarCloud quality gate

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Duplicate templates in Widget Lab with automatic "(copy)" naming
    • Test Query button to run/validate template queries, with loading state and success/error toasts
  • Refactor

    • Centralized chart click interaction handling for more consistent behavior
    • Switched chart/widget loading to on-demand dynamic loading for improved performance
  • Tests

    • Added test coverage for click-action behavior and parameter wiring
  • Chores

    • Updated coverage exclusions for page files

alfredorubin96 and others added 8 commits March 31, 2026 13:52
- Add "Duplicate" button (Copy icon) on template cards — creates a copy
  with "(copy)" suffix via existing createTemplate mutation
- Add "Test Query" button (Play icon) on templates that have a query
  and connection — executes the query via /api/query and shows
  success/error result
- Test query button disabled while running, shows loading state

Closes #270

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…orphaned-widgets

# Conflicts:
#	app/src/lib/__tests__/chart-registry.test.ts
@alfredo1996 alfredo1996 added this to the v1.0 — Community Launch milestone Mar 31, 2026
@coderabbitai

coderabbitai Bot commented Mar 31, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 4fb13303-6055-4730-996e-f08b5e71510b

📥 Commits

Reviewing files that changed from the base of the PR and between 74d4623 and adc5167.

📒 Files selected for processing (1)
  • app/src/app/(dashboard)/widget-lab/page.tsx

Walkthrough

Adds Duplicate and Test Query actions to Widget Lab template cards, extracts chart click interaction logic into a new useClickAction hook and wires it into CardContainer, introduces lazy component loaders in chart-registry, and updates Sonar coverage exclusions.

Changes

Cohort / File(s) Summary
Widget Lab Template Actions
app/src/app/(dashboard)/widget-lab/page.tsx
Added handleDuplicate to create template copies (preserves description/tags/connection/query/settings) and handleTestQuery to POST { connectionId, query, params } to /api/query. Wired handlers and per-template loading state into TemplateCard.
Click Action Hook & Tests
app/src/hooks/use-click-action.ts, app/src/hooks/__tests__/use-click-action.test.ts
New exported useClickAction(widget, onNavigateToPage?) returning { handleChartClick, hasClickAction, clickableColumns }. Hook resolves click actions, updates parameter store, and triggers navigation. Added unit tests validating resolution, clickable columns, and parameter-store wiring.
CardContainer Integration
app/src/components/card-container.tsx
Replaced inline click-handling logic with useClickAction hook usage; now obtains handleChartClick and clickableColumns from the hook and forwards them to ChartRenderer.
Dynamic Chart Loading
app/src/lib/chart-registry.ts
ChartConfig now includes component: () => Promise<{ default: React.ComponentType<any> }> and registry entries use dynamic import() loaders for chart/widget components.
Configuration
sonar-project.properties
Added app/src/app/**/page.tsx to SonarQube coverage exclusions.

Sequence Diagram(s)

sequenceDiagram
participant UI as TemplateCard (UI)
participant Page as WidgetLabPage
participant API as /api/query
participant Toast as useToast

UI->>Page: onTestQuery(template)
Page->>API: POST /api/query {connectionId, query, params}
API-->>Page: 200 / error
Page->>Toast: show success / error
Page-->>UI: clear loading state
Loading
sequenceDiagram
participant UI as TemplateCard (UI)
participant Page as WidgetLabPage
participant API as createTemplate.mutate (client hook)
participant Store as Backend DB

UI->>Page: onDuplicate(template)
Page->>API: createTemplate.mutate({name: copiedName, description, tags, ...})
API->>Store: persist template
Store-->>API: created template
API-->>Page: mutation success
Page->>UI: update list (refetch/optimistic)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested labels

enhancement, pkg:app, area:widgets

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Out of Scope Changes check ❓ Inconclusive Chart registry changes and hook extraction appear related to broader widget improvements, but their necessity relative to #270's scope (duplicate and test query only) is unclear. Clarify whether chart-registry.ts updates and useClickAction hook extraction are dependencies for #270 or belong to the separate #291 integration mentioned in PR body.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Title clearly summarizes the main changes: orphaned widget handling, duplicate template, and test query functionality for widget management.
Linked Issues check ✅ Passed PR implements all coding objectives from #270: Duplicate button creating copies with '(copy)' suffix [#270], Test Query button running SQL/Cypher against connections with loading states [#270], and toast notifications for success/failure feedback [#270].

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch improve/widgets-integration

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 and usage tips.

Page components (app/**/page.tsx) are UI-heavy React Server/Client
components that require full browser mounts to test. Their logic is
covered via E2E tests, not unit tests. Excluding them prevents false
coverage gaps on PRs that modify page-level handlers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
app/src/lib/chart-registry.ts (1)

692-706: ⚠️ Potential issue | 🟠 Major

Component loaders are not SSR-safe and appear unused.

Per the relevant code snippet, chart-renderer.tsx lines 18-105 and 188-240 show a hardcoded switch statement with its own dynamic() imports — it does not call getChartConfig(type).component(). These loaders are dead code until the renderer is refactored.

Additionally, when this is wired up, each loader needs SSR disabled. Consider either:

  1. Return pre-wrapped dynamic components (changes signature):
import dynamic from "next/dynamic";

component: dynamic(
  () => import("@neoboard/components").then((m) => ({ default: m.BarChart })),
  { ssr: false }
),
  1. Document that the consumer must wrap with dynamic({ ssr: false }) and update chart-renderer to do so.

As per coding guidelines: "Chart components must use next/dynamic with ssr: false".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/lib/chart-registry.ts` around lines 692 - 706, The component loader
entries in chart-registry.ts (the component field returned by
getChartConfig/type entries such as bar -> component and line -> component) are
currently plain import factories, unused by chart-renderer.tsx's hardcoded
switch and unsafe for SSR; update either the registry or the renderer so chart
components are provided as Next dynamic components with ssr: false: either
change the registry's component value to return pre-wrapped dynamic components
(thus changing the signature of component entries) or modify chart-renderer.tsx
to call getChartConfig(type).component and wrap the returned loader with
next/dynamic({ ssr: false }) before rendering; ensure you also remove or wire-up
any dead loaders in chart-registry.ts so all chart types (e.g., BarChart,
LineChart) follow the "Chart components must use next/dynamic with ssr: false"
guideline.
🧹 Nitpick comments (1)
app/src/app/(dashboard)/widget-lab/page.tsx (1)

274-278: Consider replacing alert() with a toast notification.

Native alert() blocks the main thread and cannot be styled. A toast would provide better UX and align with the PR objective of "appropriate UI states."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/app/`(dashboard)/widget-lab/page.tsx around lines 274 - 278, Replace
the blocking alert() calls in the query response handling with non-blocking
toast notifications: instead of alert("Query executed successfully."), call your
app's toast success method (e.g., toast.success("Query executed
successfully.")), and instead of alert(`Query failed: ${err.error?.message ??
res.statusText}`) call toast.error(...) with the same message; import and use
the project's toast utility (or add one like react-hot-toast) at the top of the
file and ensure you surface err.error?.message ?? res.statusText in the toast
error message so the user sees the same error details.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/app/`(dashboard)/widget-lab/page.tsx:
- Around line 273-282: The current error handling calls await res.json()
directly and will throw if the response body isn't JSON; update the non-OK
branch that uses the res variable to attempt JSON parsing in a try/catch (e.g.,
try { const err = await res.json(); ... } catch { const text = await
res.text().catch(() => null); ... }) and then use the parsed err.error?.message
if available, else fall back to the plain text or res.statusText so the alert
shows a meaningful message for non-JSON responses; make this change around the
existing if (res.ok) { ... } else { ... } block.

In `@app/src/hooks/__tests__/use-click-action.test.ts`:
- Around line 63-66: The test currently uses a conditional guard "if
(result?.setParameter)" which can let the test pass with zero assertions;
instead assert that result.setParameter exists explicitly (e.g.,
expect(result?.setParameter).toBeDefined()/not.toBeNull()) before checking its
fields, then assert result.setParameter.parameterName and
result.setParameter.value; update the test referencing the same symbols (result
and setParameter) so missing setParameter fails the test rather than silently
skipping assertions.
- Around line 23-27: Tests only assert the exported type of useClickAction and
never exercise the hook or its internal handler, so add tests that mount the
hook (e.g., using renderHook or a test component) to obtain the returned values,
call the returned handleChartClick with representative event/payloads, and
assert the expected side effects (callback invocation, store updates, or
dispatched actions) and cleanup; specifically locate and exercise useClickAction
and its returned handleChartClick, mock any dependencies/callbacks (e.g.,
onClick handlers or store selectors) and verify they are invoked with correct
args and that the hook wiring works end-to-end.

In `@app/src/lib/chart-registry.ts`:
- Around line 44-52: The `component` field in chart-registry.ts is dead or
incorrectly implemented: either remove the `component: () => Promise<{ default:
React.ComponentType<any> }>` property from the registry entries, or refactor it
to return a next/dynamic loader that disables SSR and then update chart-renderer
to consume that loader instead of its hardcoded dynamic imports; specifically,
if keeping it, make `component` provide a next/dynamic(...) result with { ssr:
false } (so chart-renderer can render without SSR) and ensure chart-renderer
uses the registry's `component` property rather than its internal imports,
otherwise delete all references to `component` and any comments claiming
chart-renderer uses it.

---

Outside diff comments:
In `@app/src/lib/chart-registry.ts`:
- Around line 692-706: The component loader entries in chart-registry.ts (the
component field returned by getChartConfig/type entries such as bar -> component
and line -> component) are currently plain import factories, unused by
chart-renderer.tsx's hardcoded switch and unsafe for SSR; update either the
registry or the renderer so chart components are provided as Next dynamic
components with ssr: false: either change the registry's component value to
return pre-wrapped dynamic components (thus changing the signature of component
entries) or modify chart-renderer.tsx to call getChartConfig(type).component and
wrap the returned loader with next/dynamic({ ssr: false }) before rendering;
ensure you also remove or wire-up any dead loaders in chart-registry.ts so all
chart types (e.g., BarChart, LineChart) follow the "Chart components must use
next/dynamic with ssr: false" guideline.

---

Nitpick comments:
In `@app/src/app/`(dashboard)/widget-lab/page.tsx:
- Around line 274-278: Replace the blocking alert() calls in the query response
handling with non-blocking toast notifications: instead of alert("Query executed
successfully."), call your app's toast success method (e.g.,
toast.success("Query executed successfully.")), and instead of alert(`Query
failed: ${err.error?.message ?? res.statusText}`) call toast.error(...) with the
same message; import and use the project's toast utility (or add one like
react-hot-toast) at the top of the file and ensure you surface
err.error?.message ?? res.statusText in the toast error message so the user sees
the same error details.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 544ab259-6ec3-4e47-8e82-ccaa1715eea4

📥 Commits

Reviewing files that changed from the base of the PR and between dc8d59f and 74d4623.

📒 Files selected for processing (6)
  • app/src/app/(dashboard)/widget-lab/page.tsx
  • app/src/components/card-container.tsx
  • app/src/hooks/__tests__/use-click-action.test.ts
  • app/src/hooks/use-click-action.ts
  • app/src/lib/chart-registry.ts
  • sonar-project.properties

Comment on lines +273 to +282
if (res.ok) {
alert("Query executed successfully.");
} else {
const err = await res.json();
alert(`Query failed: ${err.error?.message ?? res.statusText}`);
}
} catch (e) {
alert(
`Query failed: ${e instanceof Error ? e.message : "Unknown error"}`,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Guard against non-JSON error responses.

If the server returns a non-JSON response (e.g., a 502 gateway error with HTML), res.json() will throw, and the user will see a cryptic "Unexpected token" message instead of the actual status.

Proposed fix
       if (res.ok) {
         alert("Query executed successfully.");
       } else {
-        const err = await res.json();
-        alert(`Query failed: ${err.error?.message ?? res.statusText}`);
+        let message = res.statusText;
+        try {
+          const err = await res.json();
+          message = err.error?.message ?? message;
+        } catch {
+          // Response wasn't JSON — use statusText
+        }
+        alert(`Query failed: ${message}`);
       }
📝 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
if (res.ok) {
alert("Query executed successfully.");
} else {
const err = await res.json();
alert(`Query failed: ${err.error?.message ?? res.statusText}`);
}
} catch (e) {
alert(
`Query failed: ${e instanceof Error ? e.message : "Unknown error"}`,
);
if (res.ok) {
alert("Query executed successfully.");
} else {
let message = res.statusText;
try {
const err = await res.json();
message = err.error?.message ?? message;
} catch {
// Response wasn't JSON — use statusText
}
alert(`Query failed: ${message}`);
}
} catch (e) {
alert(
`Query failed: ${e instanceof Error ? e.message : "Unknown error"}`,
);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/app/`(dashboard)/widget-lab/page.tsx around lines 273 - 282, The
current error handling calls await res.json() directly and will throw if the
response body isn't JSON; update the non-OK branch that uses the res variable to
attempt JSON parsing in a try/catch (e.g., try { const err = await res.json();
... } catch { const text = await res.text().catch(() => null); ... }) and then
use the parsed err.error?.message if available, else fall back to the plain text
or res.statusText so the alert shows a meaningful message for non-JSON
responses; make this change around the existing if (res.ok) { ... } else { ... }
block.

Comment thread app/src/hooks/__tests__/use-click-action.test.ts
Comment thread app/src/hooks/__tests__/use-click-action.test.ts
Comment thread app/src/lib/chart-registry.ts Outdated
Comment on lines +44 to +52
/**
* Lazy component loader for this chart type. Used by chart-renderer
* to dynamically import the component. Returns a module with a default export.
*
* For charts that don't need lazy loading (e.g., JSON, Markdown), this
* can return the component directly wrapped in `{ default: Component }`.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
component: () => Promise<{ default: React.ComponentType<any> }>;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if the registry's component field is actually used
rg -nP --type=ts --type=tsx '\.component\s*\(' -g '!chart-registry.ts'
rg -nP --type=ts 'getChartConfig.*\.component'
ast-grep --pattern 'getChartConfig($_).component'

Repository: alfredo1996/neoboard

Length of output: 91


🏁 Script executed:

#!/bin/bash
# Search with correct syntax - rg supports -t ts for TypeScript files
rg '\.component\s*\(' --type ts -g '!chart-registry.ts'

# Also search for direct component field access
rg 'getChartConfig.*\.component' --type ts

# Search for imports or references to component field
rg '\bcomponent\b' app/src/lib/ --type ts -B 2 -A 2 | head -100

Repository: alfredo1996/neoboard

Length of output: 5717


🏁 Script executed:

#!/bin/bash
# Check how chart-renderer.tsx handles components
rg -l 'chart-renderer' --type ts app/src/
cat app/src/lib/chart-renderer.tsx 2>/dev/null | head -150

Repository: alfredo1996/neoboard

Length of output: 228


🏁 Script executed:

#!/bin/bash
# Read chart-renderer.tsx to see how it consumes the registry
cat app/src/components/chart-renderer.tsx | head -200

Repository: alfredo1996/neoboard

Length of output: 6625


🏁 Script executed:

#!/bin/bash
# Search for any usage of getChartConfig or registry component in chart-renderer
rg 'getChartConfig|component.*from.*registry|\.component' app/src/components/chart-renderer.tsx --type ts -B 3 -A 3

Repository: alfredo1996/neoboard

Length of output: 46


🏁 Script executed:

#!/bin/bash
# Search entire codebase for any reference to .component on chart config objects
rg 'config\.component|getChartConfig.*\.component|ChartConfig.*\.component' --type ts

# Also check if there are any imports or references to this field specifically
rg '\bcomponent\b.*Promise.*default' app/src --type ts | grep -v chart-registry

Repository: alfredo1996/neoboard

Length of output: 46


Remove unused component field or properly integrate it with next/dynamic.

The component loaders in chart-registry.ts use plain import() without next/dynamic({ ssr: false }), violating the requirement that chart components must disable SSR. Additionally, the comment claiming this field is "used by chart-renderer" is inaccurate—chart-renderer uses its own hardcoded dynamic imports instead. The field is dead code and should either be removed or refactored to be properly consumed by chart-renderer with SSR disabled.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/lib/chart-registry.ts` around lines 44 - 52, The `component` field in
chart-registry.ts is dead or incorrectly implemented: either remove the
`component: () => Promise<{ default: React.ComponentType<any> }>` property from
the registry entries, or refactor it to return a next/dynamic loader that
disables SSR and then update chart-renderer to consume that loader instead of
its hardcoded dynamic imports; specifically, if keeping it, make `component`
provide a next/dynamic(...) result with { ssr: false } (so chart-renderer can
render without SSR) and ensure chart-renderer uses the registry's `component`
property rather than its internal imports, otherwise delete all references to
`component` and any comments claiming chart-renderer uses it.

…p copy suffix

- Add AbortSignal.timeout(30_000) to handleTestQuery fetch call
- Replace all alert() calls with toast notifications (useToast from @neoboard/components)
- Handle TimeoutError specifically with a dedicated message
- Add handleDuplicate with (copy) suffix deduplication via regex strip
- Add onSuccess/onError callbacks to createTemplate.mutate in handleDuplicate

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot
0.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

…s-integration

# Conflicts:
#	app/src/lib/__tests__/chart-registry.test.ts
@alfredo1996
alfredo1996 merged commit d844703 into release/1.0 Mar 31, 2026
1 check passed
@alfredo1996
alfredo1996 deleted the improve/widgets-integration branch April 7, 2026 11:47
alfredo1996 added a commit that referenced this pull request May 10, 2026
improve: widgets — orphaned widget handling, duplicate template, test query
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.

2 participants