Repository navigation
feat: enhanced dasboard creation - #651
Conversation
Co-authored-by: Copilot <[email protected]>
…ual options Co-authored-by: Copilot <[email protected]>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (22)
💤 Files with no reviewable changes (2)
✅ Files skipped from review due to trivial changes (5)
📝 WalkthroughWalkthroughAdds a pluggable dashboard add-panel action system, deck map utilities and UI for configuring lon/lat columns, centralizes column metadata via ColumnsContext, provides initial-state AI/manual dashboard builders, decouples panel source wiring, and introduces AiSlice.startNewSession plus CLI/store wiring updates. ChangesDashboard Panel Refactor and AI Integration
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
Actionable comments posted: 9
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/mosaic/src/dashboard/MosaicDashboardContext.tsx (1)
2-12:⚠️ Potential issue | 🔴 Critical | ⚡ Quick winBreak the context ↔ initial-state import cycle.
Line 2 introduces a dependency that is already failing CI with a circular import chain. Move the
onStartcallback type into a neutral shared type module (e.g., underdashboard/types) and import that type from both sides instead of importing fromMosaicDashboardInitialState.As per coding guidelines, "Keep the compound context layer thin and stable. Avoid turning a compound provider into a second state system; it should usually wrap an existing hook/store API rather than replace it".
🤖 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/mosaic/src/dashboard/MosaicDashboardContext.tsx` around lines 2 - 12, MosaicDashboardContextValue currently imports MosaicDashboardInitialStateProps to reference onStart, creating a circular import; extract the onStart callback type into a neutral shared type (e.g., create a new export like DashboardTypes or dashboard/types that declares OnStart type) and update both MosaicDashboardContextValue and MosaicDashboardInitialState to import that shared OnStart type instead of importing MosaicDashboardInitialStateProps; ensure the exported type name is used in the existing context type (onStart?: OnStart) and in the initial-state file to break the cycle and keep the context layer thin.
🧹 Nitpick comments (3)
packages/mosaic/src/chart-builders/TableSelector.tsx (1)
17-22: ⚡ Quick winRemove or implement the
requiredprop contract.
requiredis exposed in the public props but has no effect in rendering or behavior, which makes the API misleading.Proposed cleanup
export interface TableSelectorProps { value: string | undefined; onChange?: (tableName: string) => void; placeholder?: string; - required?: boolean; }🤖 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/mosaic/src/chart-builders/TableSelector.tsx` around lines 17 - 22, The TableSelectorProps exposes a required?: boolean that is never used; remove the misleading prop from the public contract by deleting required from the TableSelectorProps interface and any related defaultProps/prop spreads, and update the TableSelector component signature (and any places that pass required) to stop accepting it; run and fix any consumer call sites or tests that referenced it. If you prefer to keep behavior instead, implement it by passing the prop into the underlying form control (e.g., the <select> or input inside TableSelector) as the HTML required attribute and optionally render a visual required marker, but do one consistent change across TableSelector, its props interface, and all consumers.packages/mosaic/src/chart/chart-settings/ChartTypeSelector.tsx (1)
33-35: ⚡ Quick winAdd an explicit accessible name to the trigger.
After removing the wrapper label, provide a guaranteed control name on the trigger (
aria-labeloraria-labelledby) to avoid unlabeled-select scenarios.Suggested diff
- <SelectTrigger className="h-8 text-xs"> + <SelectTrigger className="h-8 text-xs" aria-label="Chart type">🤖 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/mosaic/src/chart/chart-settings/ChartTypeSelector.tsx` around lines 33 - 35, ChartTypeSelector's SelectTrigger is missing an accessible name after the wrapper label was removed, causing an unlabeled select; add a guaranteed accessible name by adding an aria-label or aria-labelledby to the SelectTrigger element (e.g., in the SelectTrigger JSX inside ChartTypeSelector) so the dropdown control is always announced by screen readers—use a clear string like "Chart type" or point aria-labelledby to an existing visible label element if present.packages/mosaic/src/chart-builders/ColumnsContext.tsx (1)
30-38: ⚡ Quick winMemoize the context value to keep provider updates stable.
value={{columns, tableName}}creates a new object every render and can force unnecessary rerenders in all consumers.Suggested diff
import { createContext, useContext, + useMemo, type FC, type PropsWithChildren, } from 'react'; @@ export const ColumnsProvider: FC<ColumnsProviderProps> = ({ columns, tableName, children, }) => { + const value = useMemo(() => ({columns, tableName}), [columns, tableName]); return ( - <ColumnsContext.Provider value={{columns, tableName}}> + <ColumnsContext.Provider value={value}> {children} </ColumnsContext.Provider> ); };As per coding guidelines, "Keep the compound context layer thin and stable. Avoid turning a compound provider into a second state system; it should usually wrap an existing hook/store API rather than replace it".
🤖 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/mosaic/src/chart-builders/ColumnsContext.tsx` around lines 30 - 38, ColumnsProvider is passing value={{columns, tableName}} directly which recreates the object each render and can trigger unnecessary consumer rerenders; wrap the context value in useMemo (e.g., memoize {columns, tableName} with deps [columns, tableName]) and pass that memoized value to ColumnsContext.Provider in the ColumnsProvider component to keep provider updates stable.
🤖 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/ai-core/src/AiSlice.ts`:
- Around line 1116-1140: The hardcoded setTimeout in startNewSession causes race
conditions; replace it with a readiness-based wait that proceeds only when the
session's chat provider has registered its sendMessage handler (or an equivalent
"chat ready" flag). Locate startNewSession and remove the
setTimeout/tryStartAnalysis pattern; instead implement a short-polling loop or
awaitable event that checks the newly created session (via
get().ai.getCurrentSession() or get().ai.getSession(session.id)) until the
chat/sendMessage registration is present (or until a reasonable timeout), then
call get().ai.startAnalysis(session.id); ensure you reference the session.id and
the chat registration check (e.g., session.chatProvider?.sendMessage or a new
get().ai.isChatReady(session.id)) so startup is deterministic.
In `@packages/deck/src/LatitudeSelector.tsx`:
- Around line 27-36: The handler in LatitudeSelector (handleChange) currently
returns early if currentLongitudeColumn is falsy, preventing initializing
latitude first; remove that early return and let the function call
regenerateMapConfigForTable(panel, currentTable, currentLongitudeColumn,
latitudeColumn) even when currentLongitudeColumn is undefined so users can set
the first coordinate, matching the mirrored behavior in LongitudeSelector;
ensure state update flow that follows this call remains the same so the new
config is applied when either coordinate is set first.
In `@packages/deck/src/mapConfigUtils.ts`:
- Around line 55-92: The function currently returns a brand-new config that
replaces panel.config and discards existing map-level settings; instead
merge/retain existing panel.config and its nested spec fields when regenerating
coordinates: obtain the current config (panel.config), spread it into the
returned object and merge/override only the specific keys you must change
(spec.initialViewState and spec.layers, datasets[datasetId], fitToData) while
preserving properties like mapStyle, mapProps, showLegends, interaction; update
the logic around the returned object (the block creating spec, datasets and
fitToData) to shallow-merge with existing config and spec rather than replacing
them wholesale.
In `@packages/mosaic/src/chart/addChartPanelAction.ts`:
- Around line 12-16: The createPanel implementation currently hard-codes
'histogram' as the initial chartType, which can be invalid; change createPanel
to read the available chartTypes from the action context (the same context used
by isEnabled) and pick a valid initial value (e.g., chartTypes[0] or a
configured default) before calling createMosaicDashboardChartPanelConfig so the
new panel's chartType is always one of the provided chartTypes; update
references to isEnabled, createPanel, and createMosaicDashboardChartPanelConfig
accordingly to use the derived chartType.
In `@packages/mosaic/src/dashboard/initial-state/BuildDashboardManuallyPanel.tsx`:
- Around line 30-34: Add an early-return guard that checks the current
isStarting flag at the top of handleStart (and the analogous handler around
lines 52-59) so the function exits immediately if a manual start is already in
progress; specifically, in BuildDashboardManuallyPanel update handleStart to
return if isStarting is true before calling onStartingChange(true) or running
the try block, and do the same guard for the other manual-creation handler,
referencing the isStarting prop/state and onStartingChange callback to prevent
duplicate submissions.
In `@packages/mosaic/src/dashboard/initial-state/BuildDashboardWithAIPanel.tsx`:
- Around line 16-27: handleStart can be invoked re-entrantly (e.g., via
keyboard) before the UI blocks input; add a guard that prevents multiple
concurrent starts by tracking an "in-progress" flag. Modify handleStart to
return immediately if a local isStarting boolean/ref is true, set it true before
awaiting onStart(prompt) and only clear it in the finally block (alongside
calling onStartingChange(false)); reference the existing symbols handleStart,
prompt, onStart, onStartingChange and use a local ref or state (e.g.,
isStartingRef) so rapid repeated calls are ignored.
In `@packages/mosaic/src/dashboard/initial-state/MosaicDashboardInitialState.tsx`:
- Around line 19-21: Update the user-facing subtitle copy in the
MosaicDashboardInitialState component: replace the <p> text content "Choose how
would you like to start" with the corrected phrasing "Choose how you'd like to
start" (the <p> element using className "text-muted-foreground text-sm"). Ensure
the change is made in MosaicDashboardInitialState.tsx so the displayed string is
updated in the rendered JSX.
In `@packages/mosaic/src/dashboard/MosaicDashboard.tsx`:
- Around line 89-93: The guard for enabling chart creation incorrectly treats
chartTypes undefined as truthy because `chartTypes?.length !== 0` is true when
chartTypes is undefined; update the condition in the `canCreateChart`
calculation(s) so undefined yields false (for example replace
`chartTypes?.length !== 0` with `(chartTypes?.length ?? 0) !== 0` or
`Array.isArray(chartTypes) && chartTypes.length > 0`). Apply the same change to
the other occurrence referenced (the second `canCreateChart` occurrence) and
keep references to `selectedTableInfo`, `panelRenderers`, and
`MOSAIC_DASHBOARD_CHART_PANEL_TYPE` intact.
In `@packages/mosaic/src/dashboard/MosaicDashboardSlice.ts`:
- Around line 35-36: MosaicDashboardSlice.ts currently imports
MosaicDashboardAddPanelAction types creating a circular dependency; extract the
shared action/context types used by both MosaicDashboardSlice and
MosaicDashboardAddPanelAction into a new leaf module (e.g.,
MosaicDashboard.types.ts), update both files to import those types from that new
module, and remove any direct imports from MosaicDashboardAddPanelAction in
MosaicDashboardSlice (and vice versa) so the slice and add-panel files no longer
reference each other directly.
---
Outside diff comments:
In `@packages/mosaic/src/dashboard/MosaicDashboardContext.tsx`:
- Around line 2-12: MosaicDashboardContextValue currently imports
MosaicDashboardInitialStateProps to reference onStart, creating a circular
import; extract the onStart callback type into a neutral shared type (e.g.,
create a new export like DashboardTypes or dashboard/types that declares OnStart
type) and update both MosaicDashboardContextValue and
MosaicDashboardInitialState to import that shared OnStart type instead of
importing MosaicDashboardInitialStateProps; ensure the exported type name is
used in the existing context type (onStart?: OnStart) and in the initial-state
file to break the cycle and keep the context layer thin.
---
Nitpick comments:
In `@packages/mosaic/src/chart-builders/ColumnsContext.tsx`:
- Around line 30-38: ColumnsProvider is passing value={{columns, tableName}}
directly which recreates the object each render and can trigger unnecessary
consumer rerenders; wrap the context value in useMemo (e.g., memoize {columns,
tableName} with deps [columns, tableName]) and pass that memoized value to
ColumnsContext.Provider in the ColumnsProvider component to keep provider
updates stable.
In `@packages/mosaic/src/chart-builders/TableSelector.tsx`:
- Around line 17-22: The TableSelectorProps exposes a required?: boolean that is
never used; remove the misleading prop from the public contract by deleting
required from the TableSelectorProps interface and any related defaultProps/prop
spreads, and update the TableSelector component signature (and any places that
pass required) to stop accepting it; run and fix any consumer call sites or
tests that referenced it. If you prefer to keep behavior instead, implement it
by passing the prop into the underlying form control (e.g., the <select> or
input inside TableSelector) as the HTML required attribute and optionally render
a visual required marker, but do one consistent change across TableSelector, its
props interface, and all consumers.
In `@packages/mosaic/src/chart/chart-settings/ChartTypeSelector.tsx`:
- Around line 33-35: ChartTypeSelector's SelectTrigger is missing an accessible
name after the wrapper label was removed, causing an unlabeled select; add a
guaranteed accessible name by adding an aria-label or aria-labelledby to the
SelectTrigger element (e.g., in the SelectTrigger JSX inside ChartTypeSelector)
so the dropdown control is always announced by screen readers—use a clear string
like "Chart type" or point aria-labelledby to an existing visible label element
if present.
🪄 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: 6e994cb5-b287-45c6-8a0e-fc90a5bdefc0
📒 Files selected for processing (46)
apps/sqlrooms-cli-ui/src/store.tsapps/sqlrooms-cli-ui/src/workspace/dashboard/DashboardArtifact.tsxpackages/ai-core/src/AiSlice.tspackages/deck/src/LatitudeSelector.tsxpackages/deck/src/LongitudeSelector.tsxpackages/deck/src/MapSettings.tsxpackages/deck/src/dashboard.tsxpackages/deck/src/dashboardConfig.tspackages/deck/src/index.tspackages/deck/src/mapConfigUtils.tspackages/mosaic/src/chart-builders/ColumnSelector.tsxpackages/mosaic/src/chart-builders/ColumnsContext.tsxpackages/mosaic/src/chart-builders/MultiFieldSelector.tsxpackages/mosaic/src/chart-builders/TableSelector.tsxpackages/mosaic/src/chart-types/line-chart/LineChartSettings.tsxpackages/mosaic/src/chart-types/tool-helpers.tspackages/mosaic/src/chart/MosaicDashboardChartRenderer.tsxpackages/mosaic/src/chart/addChartPanelAction.tspackages/mosaic/src/chart/chart-settings/ChartSettings.tsxpackages/mosaic/src/chart/chart-settings/ChartSettingsContext.tsxpackages/mosaic/src/chart/chart-settings/ChartTypeSelector.tsxpackages/mosaic/src/dashboard/MosaicDashboard.tsxpackages/mosaic/src/dashboard/MosaicDashboardAddPanelAction.tspackages/mosaic/src/dashboard/MosaicDashboardContext.tsxpackages/mosaic/src/dashboard/MosaicDashboardEmptyState.tsxpackages/mosaic/src/dashboard/MosaicDashboardPanel.tsxpackages/mosaic/src/dashboard/MosaicDashboardPanelHeader.tsxpackages/mosaic/src/dashboard/MosaicDashboardPanels.tsxpackages/mosaic/src/dashboard/MosaicDashboardSlice.tspackages/mosaic/src/dashboard/MosaicDashboardToolbar.tsxpackages/mosaic/src/dashboard/defaultPanelActions.tspackages/mosaic/src/dashboard/initial-state/BuildDashboardManuallyPanel.tsxpackages/mosaic/src/dashboard/initial-state/BuildDashboardWithAIPanel.tsxpackages/mosaic/src/dashboard/initial-state/MosaicDashboardInitialState.tsxpackages/mosaic/src/dashboard/toolbar/MosaicDashboardAddPanelDropdown.tsxpackages/mosaic/src/dashboard/toolbar/MosaicDashboardResetFiltersButton.tsxpackages/mosaic/src/dashboard/toolbar/MosaicDashboardToolbar.tsxpackages/mosaic/src/dashboard/useAddPanelActions.tspackages/mosaic/src/dashboard/useMosaicDashboardAddPanelActionContext.tspackages/mosaic/src/dashboard/useSelectedOrFirstTable.tspackages/mosaic/src/dashboard/useTablesWithColumns.tspackages/mosaic/src/index.tspackages/mosaic/src/profiler/MosaicDashboardProfilerPanelRenderer.tsxpackages/mosaic/src/profiler/addProfilerPanelAction.tspackages/mosaic/src/text/addTextPanelAction.tspackages/ui/src/components/tree.tsx
💤 Files with no reviewable changes (3)
- packages/mosaic/src/dashboard/MosaicDashboardToolbar.tsx
- packages/mosaic/src/dashboard/MosaicDashboardPanel.tsx
- packages/mosaic/src/chart-types/tool-helpers.ts
There was a problem hiding this comment.
♻️ Duplicate comments (1)
packages/ai-core/src/AiSlice.ts (1)
1149-1177:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
startNewSessionresolves before startup actually completes.This code fire-and-forgets readiness polling (
void ...) and uses timer recursion, so awaitingstartNewSessiondoes not wait for analysis start/timeout. That can desync UIisStartingflows and error handling.Proposed fix
- const waitForChatReadyAndStart = async (): Promise<void> => { - const maxAttempts = 50; // 50 * 20ms = 1 second max - let attempts = 0; - - const checkAndStart = async () => { - const sendMessage = get().ai.getChatSendMessage(session.id); - if (sendMessage) { - // Chat provider is ready, start analysis - await get().ai.startAnalysis(session.id); - return; - } - - attempts++; - if (attempts >= maxAttempts) { - console.error( - 'Timeout waiting for chat provider to register for session:', - session.id, - ); - return; - } - - // Poll again after a short delay - setTimeout(checkAndStart, 20); - }; - - await checkAndStart(); - }; - - void waitForChatReadyAndStart(); + const waitForChatReadyAndStart = async (): Promise<void> => { + const maxAttempts = 50; // 50 * 20ms = 1 second max + for (let attempts = 0; attempts < maxAttempts; attempts++) { + if (get().ai.getChatSendMessage(session.id)) { + await get().ai.startAnalysis(session.id); + return; + } + await new Promise((resolve) => setTimeout(resolve, 20)); + } + + console.error( + 'Timeout waiting for chat provider to register for session:', + session.id, + ); + }; + + await waitForChatReadyAndStart();🤖 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/ai-core/src/AiSlice.ts` around lines 1149 - 1177, The readiness poll in waitForChatReadyAndStart currently fire-and-forgets (void ...) and uses timer recursion so startNewSession returns before analysis starts; change waitForChatReadyAndStart into an awaitable Promise<void> and convert the recursive setTimeout into an async polling loop (e.g., while loop with await delay) that checks get().ai.getChatSendMessage(session.id) up to maxAttempts and then either awaits get().ai.startAnalysis(session.id) or resolves/rejects on timeout; then replace the void call with await waitForChatReadyAndStart() in startNewSession so callers only proceed when startAnalysis has completed or timed out.
🤖 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.
Duplicate comments:
In `@packages/ai-core/src/AiSlice.ts`:
- Around line 1149-1177: The readiness poll in waitForChatReadyAndStart
currently fire-and-forgets (void ...) and uses timer recursion so
startNewSession returns before analysis starts; change waitForChatReadyAndStart
into an awaitable Promise<void> and convert the recursive setTimeout into an
async polling loop (e.g., while loop with await delay) that checks
get().ai.getChatSendMessage(session.id) up to maxAttempts and then either awaits
get().ai.startAnalysis(session.id) or resolves/rejects on timeout; then replace
the void call with await waitForChatReadyAndStart() in startNewSession so
callers only proceed when startAnalysis has completed or timed out.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e10ce121-f9d7-44ae-8fc1-e0b072601ad9
📒 Files selected for processing (23)
apps/sqlrooms-cli-ui/src/store.tspackages/ai-core/src/AiSlice.tspackages/deck/src/LatitudeSelector.tsxpackages/deck/src/mapConfigUtils.tspackages/mosaic/src/chart-builders/ColumnsContext.tsxpackages/mosaic/src/chart-builders/TableSelector.tsxpackages/mosaic/src/chart/addChartPanelAction.tspackages/mosaic/src/chart/chart-settings/ChartTypeSelector.tsxpackages/mosaic/src/dashboard/MosaicDashboard.tsxpackages/mosaic/src/dashboard/MosaicDashboardContext.tsxpackages/mosaic/src/dashboard/MosaicDashboardSlice.tspackages/mosaic/src/dashboard/dashboard-types.tspackages/mosaic/src/dashboard/defaultPanelActions.tspackages/mosaic/src/dashboard/initial-state/BuildDashboardManuallyPanel.tsxpackages/mosaic/src/dashboard/initial-state/BuildDashboardWithAIPanel.tsxpackages/mosaic/src/dashboard/initial-state/MosaicDashboardInitialState.tsxpackages/mosaic/src/dashboard/toolbar/MosaicDashboardResetFiltersButton.tsxpackages/mosaic/src/dashboard/toolbar/MosaicDashboardToolbar.tsxpackages/mosaic/src/dashboard/useAddPanelActions.tspackages/mosaic/src/dashboard/useMosaicDashboardAddPanelActionContext.tspackages/mosaic/src/index.tspackages/mosaic/src/profiler/addProfilerPanelAction.tspackages/mosaic/src/text/addTextPanelAction.ts
💤 Files with no reviewable changes (2)
- packages/mosaic/src/chart-builders/TableSelector.tsx
- packages/deck/src/LatitudeSelector.tsx
Summary by CodeRabbit
New Features
Improvements