feat: refresh analysis charts and dashboard feedback gating#7915
Conversation
Unify chart create and edit flows, update dashboard chart interactions, and add feedback-record availability checks with dedicated empty-state handling across analysis entry points. Made-with: Cursor
Load feedback record directories in dashboard detail page and thread them through dashboard detail and control bar components so chart dialogs no longer crash with an undefined directories reference. Made-with: Cursor
Promote Add charts to a labeled primary button in the dashboard control bar and keep secondary actions in the icon toolbar for clearer chart-creation affordance. Made-with: Cursor
Made-with: Cursor
🚨 PR Size WarningThis PR has approximately 1088 lines of changes (682 additions, 406 deletions across 22 files). Large PRs (>800 lines) are significantly harder to review and increase the chance of merge conflicts. Consider splitting this into smaller, self-contained PRs. 💡 Suggestions:
📊 What was counted:
📚 Guidelines:
If this large PR is unavoidable (e.g., migration, dependency update, major refactor), please explain in the PR description why it couldn't be split. |
WalkthroughThis pull request introduces chart creation, editing, and dashboard integration features for the analysis module. Changes include adding feedback record existence checks that gate chart and dashboard availability, consolidating chart creation and editing into a unified view, implementing automatic chart-to-dashboard assignment, introducing state management for dashboard selection and chart operations, and adding navigation support across dashboard and chart components. Several components are modified to accept new optional props for customization, a new empty state component is added for when feedback records are unavailable, and the edit-chart-view component is removed in favor of simplified creation logic. Supporting utilities for feedback record validation are introduced. 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. 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. Review rate limit: 7/8 reviews remaining, refill in 7 minutes and 30 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/web/modules/ee/analysis/charts/hooks/use-chart-dialog.ts (1)
180-188:⚠️ Potential issue | 🟠 MajorPersist the selected directory on chart updates.
The edit path updates
name,type, andquery, but it never writesselectedDirectoryIdback. If the unified dialog lets users switch data sources while editing, the saved chart keeps the oldfeedbackRecordDirectoryIdand later executions run against the wrong directory.Suggested fix
const result = await updateChartAction({ workspaceId, chartId: currentChartId, chartUpdateInput: { name: chartName.trim(), type: chartData.chartType, query: chartData.query, config: {}, + feedbackRecordDirectoryId: selectedDirectoryId, }, });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/modules/ee/analysis/charts/hooks/use-chart-dialog.ts` around lines 180 - 188, The updateChartAction call currently updates name/type/query but omits persisting the selected directory; include the selectedDirectoryId in the chartUpdateInput so edits save the new directory. Specifically, in the updateChartAction(...) invocation (the chartUpdateInput object used with updateChartAction, currentChartId, workspaceId, and chartData), add selectedDirectoryId: chartData.selectedDirectoryId (or the appropriate feedbackRecordDirectoryId property from chartData) so the saved chart reflects the user's chosen directory.apps/web/modules/ee/analysis/dashboards/components/dashboard-detail-client.tsx (1)
163-163:⚠️ Potential issue | 🟠 MajorReconcile
draftWidgetsafter chart-edit refresh.When a dashboard already has local draft state,
widgetsreads fromdraftWidgets, masking refreshed server updates. The chart save success handler callsrouter.refresh()but does not cleardraftWidgets, leaving the dashboard showing the old chart name/query until edit mode is cancelled or the page fully reloads. Either patch the matching draft widget ononSuccessor clear and rehydratedraftWidgetsfrom the refresheddashboard.widgetswhile preserving layout edits. Reproduce by entering dashboard edit mode, renaming a chart from the widget menu, saving, and verifying the widget header still shows the old chart name.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/modules/ee/analysis/dashboards/components/dashboard-detail-client.tsx` at line 163, The dashboard uses draftWidgets (const widgets = draftWidgets ?? dashboard.widgets) which masks refreshed server-side changes after a chart save; after the chart save success handler calls router.refresh(), update the draft state in that onSuccess path by either patching the matching draft widget (find by widget.id/widget.visualizationId and update its title/query) or by clearing and rehydrating draftWidgets from the newly refreshed dashboard.widgets while preserving any layout edits (e.g., keep positions/sizes from draftWidgets but replace content fields like name/query with dashboard.widgets values); implement this logic in the chart save onSuccess handler so the UI reflects the refreshed chart metadata immediately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/modules/ee/analysis/charts/components/chart-dropdown-menu.tsx`:
- Around line 41-65: The effect using getDashboardsAction inside useEffect
(guarded by isAddToDashboardDialogOpen) currently only handles resolved results
and will leave rejections unhandled; update the call to getDashboardsAction({
workspaceId }) (in the useEffect) to attach a .catch(...) to handle rejected
promises, checking the cancelled flag before calling side effects, and call
toast.error(getFormattedErrorMessage(error)) for failures; keep the existing
success branch that calls setDashboards(result.data.map(...)) and ensure the
cleanup still sets cancelled = true.
- Around line 113-133: The try block around addChartToDashboardAction needs a
catch to handle cases where the action throws (not just returns a falsy result);
wrap the await addChartToDashboardAction call with a catch that logs or captures
the error and calls toast.error(getFormattedErrorMessage(error) ||
t("workspace.analysis.charts.failed_to_add_chart_to_dashboard")), ensure you
still reset UI state as appropriate (e.g., keep setIsAddingToDashboard(false) in
finally), and avoid leaving the dialog open or selectedDashboardId set when the
call fails (consider closing the dialog or clearing selectedDashboardId in the
error path if intended); reference addChartToDashboardAction,
setIsAddingToDashboard, setIsAddToDashboardDialogOpen, setSelectedDashboardId,
and router.refresh when implementing the catch/failure handling.
In `@apps/web/modules/ee/analysis/charts/components/create-chart-view.tsx`:
- Line 110: The current chartType fallback uses DEFAULT_CHART_TYPE when
selectedChartType is undefined in edit mode; change the fallback order so in
edit mode you first use the persisted chart's type (the loaded chart object’s
type — e.g., chart?.type or loadedChart.type) before falling back to
DEFAULT_CHART_TYPE; update the assignment of chartType (and the similar logic
around lines 168-183) to: selectedChartType ?? (isEditing ? persistedChartType :
DEFAULT_CHART_TYPE) so the editor preserves the existing chart type when
editing.
In `@apps/web/modules/ee/analysis/charts/hooks/use-chart-dialog.ts`:
- Around line 221-234: The auto-add branch (when autoAddToDashboardId &&
savedChartId) currently returns early on addChartToDashboardAction failure and
leaves the newly created chart orphaned; update that branch to perform the same
cleanup as handleAddToDashboard: call the chart deletion/cleanup routine (e.g.,
deleteChartAction or the existing cleanup function used by handleAddToDashboard)
for savedChartId before returning, and include error logging/toast as before;
ensure you reference addChartToDashboardAction, savedChartId and
autoAddToDashboardId and mirror the cleanup logic from handleAddToDashboard so
the newly created chart is removed on failure to add to the dashboard.
In `@apps/web/modules/ee/analysis/lib/feedback-records.ts`:
- Line 1: The file begins with a no-op string literal "server-only"; replace
that literal with an actual import of the "server-only" module at the top of
apps/web/modules/ee/analysis/lib/feedback-records.ts so Next.js enforces the
server boundary; ensure the import is the first statement in the module (before
any other imports or code) so server-only enforcement triggers correctly.
---
Outside diff comments:
In `@apps/web/modules/ee/analysis/charts/hooks/use-chart-dialog.ts`:
- Around line 180-188: The updateChartAction call currently updates
name/type/query but omits persisting the selected directory; include the
selectedDirectoryId in the chartUpdateInput so edits save the new directory.
Specifically, in the updateChartAction(...) invocation (the chartUpdateInput
object used with updateChartAction, currentChartId, workspaceId, and chartData),
add selectedDirectoryId: chartData.selectedDirectoryId (or the appropriate
feedbackRecordDirectoryId property from chartData) so the saved chart reflects
the user's chosen directory.
In
`@apps/web/modules/ee/analysis/dashboards/components/dashboard-detail-client.tsx`:
- Line 163: The dashboard uses draftWidgets (const widgets = draftWidgets ??
dashboard.widgets) which masks refreshed server-side changes after a chart save;
after the chart save success handler calls router.refresh(), update the draft
state in that onSuccess path by either patching the matching draft widget (find
by widget.id/widget.visualizationId and update its title/query) or by clearing
and rehydrating draftWidgets from the newly refreshed dashboard.widgets while
preserving any layout edits (e.g., keep positions/sizes from draftWidgets but
replace content fields like name/query with dashboard.widgets values); implement
this logic in the chart save onSuccess handler so the UI reflects the refreshed
chart metadata immediately.
🪄 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: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 8cd7f0ad-1fda-4d83-b195-bfc509d2ac5c
⛔ Files ignored due to path filters (16)
apps/web/i18n.lockis excluded by!**/*.lockapps/web/locales/de-DE.jsonis excluded by!apps/web/locales/**apps/web/locales/en-US.jsonis excluded by!apps/web/locales/**apps/web/locales/es-ES.jsonis excluded by!apps/web/locales/**apps/web/locales/fr-FR.jsonis excluded by!apps/web/locales/**apps/web/locales/hu-HU.jsonis excluded by!apps/web/locales/**apps/web/locales/ja-JP.jsonis excluded by!apps/web/locales/**apps/web/locales/nl-NL.jsonis excluded by!apps/web/locales/**apps/web/locales/pt-BR.jsonis excluded by!apps/web/locales/**apps/web/locales/pt-PT.jsonis excluded by!apps/web/locales/**apps/web/locales/ro-RO.jsonis excluded by!apps/web/locales/**apps/web/locales/ru-RU.jsonis excluded by!apps/web/locales/**apps/web/locales/sv-SE.jsonis excluded by!apps/web/locales/**apps/web/locales/tr-TR.jsonis excluded by!apps/web/locales/**apps/web/locales/zh-Hans-CN.jsonis excluded by!apps/web/locales/**apps/web/locales/zh-Hant-TW.jsonis excluded by!apps/web/locales/**
📒 Files selected for processing (20)
apps/web/app/(app)/workspaces/[workspaceId]/components/MainNavigation.tsxapps/web/modules/ee/analysis/charts/components/add-to-dashboard-dialog.tsxapps/web/modules/ee/analysis/charts/components/advanced-chart-builder.tsxapps/web/modules/ee/analysis/charts/components/chart-dialog-footer.tsxapps/web/modules/ee/analysis/charts/components/chart-dropdown-menu.tsxapps/web/modules/ee/analysis/charts/components/charts-list-page.tsxapps/web/modules/ee/analysis/charts/components/create-chart-button.tsxapps/web/modules/ee/analysis/charts/components/create-chart-dialog.tsxapps/web/modules/ee/analysis/charts/components/create-chart-view.tsxapps/web/modules/ee/analysis/charts/components/edit-chart-view.tsxapps/web/modules/ee/analysis/charts/hooks/use-chart-dialog.tsapps/web/modules/ee/analysis/components/no-feedback-records-state.tsxapps/web/modules/ee/analysis/dashboards/components/add-existing-charts-dialog.tsxapps/web/modules/ee/analysis/dashboards/components/create-dashboard-button.tsxapps/web/modules/ee/analysis/dashboards/components/dashboard-control-bar.tsxapps/web/modules/ee/analysis/dashboards/components/dashboard-detail-client.tsxapps/web/modules/ee/analysis/dashboards/components/dashboard-widget.tsxapps/web/modules/ee/analysis/dashboards/pages/dashboard-detail-page.tsxapps/web/modules/ee/analysis/dashboards/pages/dashboards-list-page.tsxapps/web/modules/ee/analysis/lib/feedback-records.ts
💤 Files with no reviewable changes (1)
- apps/web/modules/ee/analysis/charts/components/edit-chart-view.tsx
|



Summary
Replaces #7829. Rebased onto current
epic/v5(which now includes PR1+PR2+PR3+PR4 via squash merges) so the diff shows only PR5-specific work.What changed vs #7829
epic/v5)ef1c7a064→af343b718)tweakscommit (PR3-v2 addedsource_type_label_*keys that this commit didn't know about; manually merged + dropped now-unusedworkspace.unify.sources)Validation
pnpm test→ 4604/4604 ✅pnpm i18n→ ✅tsc→ 61 errors, identical toepic/v5baseline (zero new)Test plan
pnpm --filter @formbricks/web testpnpm i18nContributes to ENG-777