mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
## Stack Depends on #49027. Followed by #49038. ## Summary - extract a controlled `QueryEditor` from the existing notebook query cell - reuse it from `QueryCell`, leaving notebook persistence and sortable-block behavior in the adapter - make table/chart result settings controlled so other query surfaces can share them - persist notebook SQL on editor blur and query execution ## To test 1. Open a notebook query cell, edit and rename it, then run the query and confirm results appear. 2. Switch between table and chart results and confirm notebook move/delete actions still work. ## Why Notebooks, query tabs, and future chat tabs need consistent query actions and result rendering without duplicating the notebook implementation. ## Impact This is primarily a refactor of the existing notebook query experience. It introduces no new query-tab routes or source-selection behavior. ## Validation - fresh non-incremental Studio TypeScript check - focused NotebookEditor component tests <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a shared query editor with SQL editing, execution, validation, visibility controls, editable titles, row limits, and loading/error states. * Added table and chart result views, including customizable bar and line charts. * Added support for switching display modes and updating chart settings. * **Improvements** * Improved query result handling and display-setting updates. * Repositioned the logarithmic-scale tooltip for better visibility. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Joshen Lim <joshenlimek@gmail.com>
104 lines
3.3 KiB
TypeScript
104 lines
3.3 KiB
TypeScript
import { useMemo } from 'react'
|
|
import { Chart, ChartBar, ChartCard, ChartContent, ChartLine } from 'ui-patterns/Chart'
|
|
|
|
import { type QueryChartConfig, type QueryResult } from '../types'
|
|
import NoDataPlaceholder from '@/components/ui/Charts/NoDataPlaceholder'
|
|
import { formatLogTick, getCumulativeResults } from '@/components/ui/QueryBlock/QueryBlock.utils'
|
|
|
|
interface QueryResultChartProps {
|
|
chart?: QueryChartConfig
|
|
result?: QueryResult
|
|
}
|
|
|
|
// [Joshen] Will need to implement log scale - refer to QueryBlock.tsx `effectiveLogScale`
|
|
// [Joshen] Will also need to implement error handling where appropriate (e.g if query errors)
|
|
|
|
const toChartValue = (value: unknown): string | number => {
|
|
if (typeof value === 'number' || typeof value === 'string') return value
|
|
if (value === null || value === undefined) return ''
|
|
return String(value)
|
|
}
|
|
|
|
export const QueryResultChart = ({ chart, result }: QueryResultChartProps) => {
|
|
const { type, x_column, y_columns = [], cumulative, show_labels, scale } = chart ?? {}
|
|
|
|
const hasConfig = !!x_column && y_columns.length > 0
|
|
const chartRows = useMemo(() => {
|
|
const xKey = x_column ?? ''
|
|
const yKey = y_columns[0] ?? ''
|
|
return (result?.rows ?? []).map((row) => ({
|
|
[xKey]: toChartValue(row[xKey]),
|
|
[yKey]: toChartValue(row[yKey]),
|
|
}))
|
|
}, [result, x_column, y_columns])
|
|
|
|
const cumulativeResults = useMemo(
|
|
() => getCumulativeResults({ rows: chartRows }, { yKey: y_columns[0] ?? '' }),
|
|
[chartRows, y_columns]
|
|
)
|
|
const resultToRender = cumulative ? cumulativeResults : chartRows
|
|
|
|
if (!result || (result?.rows && result.rows.length === 0)) {
|
|
return (
|
|
<NoDataPlaceholder
|
|
className="bg border-0"
|
|
size="normal"
|
|
message="No results"
|
|
description="Your query returned no rows"
|
|
/>
|
|
)
|
|
}
|
|
|
|
if (!hasConfig) {
|
|
return (
|
|
<NoDataPlaceholder
|
|
className="bg border-0"
|
|
size="normal"
|
|
message="Configure your chart"
|
|
description="Select your X and Y axis in the display settings"
|
|
/>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<Chart>
|
|
<ChartCard className="rounded-none border-0">
|
|
<ChartContent>
|
|
<div className="h-40">
|
|
{type === 'bar' && (
|
|
<ChartBar
|
|
isFullHeight
|
|
xKey={x_column}
|
|
dataKey={y_columns[0]}
|
|
showXAxis={show_labels}
|
|
showYAxis={show_labels}
|
|
data={resultToRender}
|
|
YAxisProps={{
|
|
scale: scale === 'log' ? 'log' : 'auto',
|
|
domain: scale === 'log' ? [1, 'auto'] : undefined,
|
|
tickFormatter: scale === 'log' ? formatLogTick : undefined,
|
|
}}
|
|
/>
|
|
)}
|
|
{type === 'line' && (
|
|
<ChartLine
|
|
isFullHeight
|
|
xKey={x_column}
|
|
dataKey={y_columns[0]}
|
|
showXAxis={show_labels}
|
|
showYAxis={show_labels}
|
|
data={resultToRender}
|
|
YAxisProps={{
|
|
scale: scale === 'log' ? 'log' : 'auto',
|
|
domain: scale === 'log' ? [1, 'auto'] : undefined,
|
|
tickFormatter: scale === 'log' ? formatLogTick : undefined,
|
|
}}
|
|
/>
|
|
)}
|
|
</div>
|
|
</ChartContent>
|
|
</ChartCard>
|
|
</Chart>
|
|
)
|
|
}
|