diff --git a/packages/cubejs-playground/src/PlaygroundQueryBuilder.tsx b/packages/cubejs-playground/src/PlaygroundQueryBuilder.tsx index 778ed6df56bd3..8817f99bad5aa 100644 --- a/packages/cubejs-playground/src/PlaygroundQueryBuilder.tsx +++ b/packages/cubejs-playground/src/PlaygroundQueryBuilder.tsx @@ -1,6 +1,6 @@ import { useState, useRef, useEffect, RefObject } from 'react'; -import { Col, Row, Divider, Typography, Space } from 'antd'; -import Icon, { LockOutlined, CloudOutlined } from '@ant-design/icons'; +import { Col, Row, Divider } from 'antd'; +import { LockOutlined, CloudOutlined } from '@ant-design/icons'; import { QueryBuilder, SchemaChangeProps, @@ -24,7 +24,7 @@ import LivePreviewBar from './components/LivePreviewContext/LivePreviewBar'; import ChartRenderer from './components/ChartRenderer/ChartRenderer'; import { SectionHeader, SectionRow } from './components'; import ChartContainer from './ChartContainer'; -import { dispatchPlaygroundEvent, formatNumber } from './utils'; +import { dispatchPlaygroundEvent } from './utils'; import { useDeepCompareMemoize, useSecurityContext, @@ -33,7 +33,7 @@ import { import { Button, Card, FatalError } from './atoms'; import { UIFramework } from './types'; import DashboardSource from './DashboardSource'; -import { LightningIcon } from './shared/icons/LightningIcon'; +import { PreAggregationStatus } from './components/PlaygroundQueryBuilder/components'; const Section = styled.div` display: flex; @@ -125,62 +125,30 @@ function PivotChangeEmitter({ return null; } +type QueryChangeEmitterProps = { + query1: Query | null; + query2: Query | null; + onChange: () => void; +}; + +function QueryChangeEmitter({ + query1, + query2, + onChange, +}: QueryChangeEmitterProps) { + useEffect(() => { + onChange(); + }, [areQueriesEqual(query1, query2)]); + + return null; +} + type THandleRunButtonClickProps = { query: Query; pivotConfig?: PivotConfig; chartType: ChartType; }; -type PreAggregationStatusProps = { - time: number; - isAggregated: boolean; -}; - -const Label = styled.div` - display: flex; - align-items: center; - padding: 2px 4px; - border-radius: 4px; - background: rgba(251, 188, 5, 0.1); -`; - -function PreAggregationStatus({ - time, - isAggregated, -}: PreAggregationStatusProps) { - const renderTime = () => ( - - {formatNumber(time)} ms - - ); - - return ( - - {isAggregated ? ( - - ) : ( - renderTime() - )} - - - Query was not accelerated with pre-aggregation {'->'} - - - - Query was accelerated with pre-aggregation - - - ); -} - export type TPlaygroundQueryBuilderProps = { apiUrl: string; cubejsToken: string; @@ -192,6 +160,11 @@ export type TPlaygroundQueryBuilderProps = { onSchemaChange?: (props: SchemaChangeProps) => void; }; +export type QueryStatus = { + timeElapsed: number; + isAggregated: boolean; +}; + export default function PlaygroundQueryBuilder({ apiUrl, cubejsToken, @@ -205,6 +178,7 @@ export default function PlaygroundQueryBuilder({ const ref = useRef(null); const queryRef = useRef(null); + const [queryStatus, setQueryStatus] = useState(null); const [framework, setFramework] = useState('react'); const [chartingLibrary, setChartingLibrary] = useState('bizcharts'); const [isChartRendererReady, setChartRendererReady] = useState(false); @@ -482,7 +456,12 @@ export default function PlaygroundQueryBuilder({ onUpdate={updatePivotConfig.update} /> - + {queryStatus ? ( + + ) : null} @@ -569,12 +548,22 @@ export default function PlaygroundQueryBuilder({ isLoading, resultSet, error, + isAggregated, + timeElapsed, }) => { if (resultSet) { setQueryError(null); + + if (isAggregated != null && timeElapsed != null) { + setQueryStatus({ + isAggregated, + timeElapsed, + }); + } } if (error) { setQueryError(error); + setQueryStatus(null); } setQueryLoading(isLoading); @@ -608,6 +597,12 @@ export default function PlaygroundQueryBuilder({ + + setQueryStatus(null)} + /> ); }} diff --git a/packages/cubejs-playground/src/components/ChartRenderer/ChartRenderer.tsx b/packages/cubejs-playground/src/components/ChartRenderer/ChartRenderer.tsx index 0d7845a71642a..3a1d83cd124ab 100644 --- a/packages/cubejs-playground/src/components/ChartRenderer/ChartRenderer.tsx +++ b/packages/cubejs-playground/src/components/ChartRenderer/ChartRenderer.tsx @@ -9,6 +9,7 @@ import type { PivotConfig, Query, ChartType } from '@cubejs-client/core'; import { Button, CubeLoader } from '../../atoms'; import { UIFramework } from '../../types'; import { event } from '../../events'; +import { QueryStatus } from '../../PlaygroundQueryBuilder'; const { Text } = Typography; @@ -69,7 +70,7 @@ export type TQueryLoadResult = { isLoading: boolean; resultSet?: ResultSet; error?: Error | null; -}; +} & Partial; type TChartRendererProps = { query: Query; @@ -99,7 +100,7 @@ export default function ChartRenderer({ onChartRendererReadyChange, onQueryStatusChange, onRunButtonClick, - onQueryChange + onQueryChange, }: TChartRendererProps) { const runButtonRef = useRef(null); const [slowQuery, setSlowQuery] = useState(false); @@ -121,19 +122,23 @@ export default function ChartRenderer({ useEffect(() => { onQueryChange(); - }, [areQueriesEqual]) + }, [areQueriesEqual]); useEffect(() => { setResultSet(false); }, [framework]); useLayoutEffect(() => { + let queryStartTime: number; window['__cubejsPlayground'] = { ...window['__cubejsPlayground'], onQueryStart: () => { + queryStartTime = Date.now(); onQueryStatusChange({ isLoading: true }); }, onQueryLoad: ({ resultSet, error }: TQueryLoadResult) => { + let isAggregated; + const timeElapsed = Date.now() - queryStartTime; if (resultSet) { const { loadResponse } = resultSet.serialize(); @@ -141,13 +146,25 @@ export default function ChartRenderer({ Boolean(loadResponse.slowQuery) && setSlowQuery(false); setResultSet(true); - const servedByPreAggregation = Object.keys(loadResponse.results[0]?.usedPreAggregations || {}).length > 0; + isAggregated = + Object.keys(loadResponse.results[0]?.usedPreAggregations || {}) + .length > 0; - event(servedByPreAggregation ? 'load_request_success_aggregated:frontend' : 'load_request_success:frontend'); + event( + isAggregated + ? 'load_request_success_aggregated:frontend' + : 'load_request_success:frontend' + ); } if (resultSet || error) { - onQueryStatusChange({ resultSet, error, isLoading: false }); + onQueryStatusChange({ + resultSet, + error, + isLoading: false, + timeElapsed, + isAggregated + }); } }, onQueryProgress: (progress) => { diff --git a/packages/cubejs-playground/src/components/PlaygroundQueryBuilder/components/PreAggregationStatus.tsx b/packages/cubejs-playground/src/components/PlaygroundQueryBuilder/components/PreAggregationStatus.tsx new file mode 100644 index 0000000000000..63f6e10955484 --- /dev/null +++ b/packages/cubejs-playground/src/components/PlaygroundQueryBuilder/components/PreAggregationStatus.tsx @@ -0,0 +1,61 @@ +import styled from 'styled-components'; +import { Space, Typography } from 'antd'; +import Icon from '@ant-design/icons'; + +import { formatNumber } from '../../../utils'; +import { LightningIcon } from '../../../shared/icons/LightningIcon'; + +type PreAggregationStatusProps = { + timeElapsed: number; + isAggregated: boolean; +}; + +const Badge = styled.div` + display: flex; + align-items: center; + padding: 2px 4px; + border-radius: 4px; + background: var(--warning-bg-color); +`; + +export function PreAggregationStatus({ + timeElapsed, + isAggregated, +}: PreAggregationStatusProps) { + const renderTime = () => ( + + {formatNumber(timeElapsed)} ms + + ); + + return ( + + {isAggregated ? ( + + + } + /> + {renderTime()} + + + ) : ( + renderTime() + )} + + {isAggregated ? ( + + Query was accelerated with pre-aggregation + + ) : ( + + Query was not accelerated with pre-aggregation {'->'} + + )} + + ); +} diff --git a/packages/cubejs-playground/src/components/PlaygroundQueryBuilder/components/index.ts b/packages/cubejs-playground/src/components/PlaygroundQueryBuilder/components/index.ts new file mode 100644 index 0000000000000..8bcdec50e3396 --- /dev/null +++ b/packages/cubejs-playground/src/components/PlaygroundQueryBuilder/components/index.ts @@ -0,0 +1 @@ +export * from './PreAggregationStatus' diff --git a/packages/cubejs-playground/src/variables-esm.js b/packages/cubejs-playground/src/variables-esm.js index 8213bf0fed6ba..c00dd79b16744 100644 --- a/packages/cubejs-playground/src/variables-esm.js +++ b/packages/cubejs-playground/src/variables-esm.js @@ -9,7 +9,9 @@ const colors = { 'dark-03': '114, 114, 144', 'dark-04': '161, 161, 181', 'dark-05': '213, 213, 226', - light: '1, 2, 251', + light: '243, 243, 251', + green: '65, 181, 111', + yellow: '251, 188, 5', }; function color(name, opacity = 1) { @@ -34,6 +36,11 @@ const VARIABLES = { 'remove-btn-hover-bg': color('purple', 0.2), 'primary-color': color('purple'), + 'success-bg-color': color('green', 0.1), + 'success-color': color('green', 0.9), + 'warning-bg-color': color('yellow', 0.1), + 'warning-color': color('yellow', 0.9), + 'pink-8': color('pink', 0.2), 'pink-9': color('pink', 0.1), diff --git a/packages/cubejs-playground/src/variables.js b/packages/cubejs-playground/src/variables.js index f09a63ac4b53f..10b0b4511ac16 100644 --- a/packages/cubejs-playground/src/variables.js +++ b/packages/cubejs-playground/src/variables.js @@ -10,6 +10,8 @@ const colors = { 'dark-04': '161, 161, 181', 'dark-05': '213, 213, 226', light: '243, 243, 251', + green: '65, 181, 111', + yellow: '251, 188, 5', }; function color(name, opacity = 1) { @@ -34,6 +36,11 @@ const VARIABLES = { 'remove-btn-hover-bg': color('purple', 0.2), 'primary-color': color('purple'), + 'success-bg-color': color('green', 0.1), + 'success-color': color('green', 0.9), + 'warning-bg-color': color('yellow', 0.1), + 'warning-color': color('yellow', 0.9), + 'pink-8': color('pink', 0.2), 'pink-9': color('pink', 0.1),