Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(editor-3001): fix tabs, error state, loading states #26881

Merged
merged 11 commits into from
Dec 13, 2024
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 16 additions & 2 deletions frontend/src/queries/nodes/DataNode/dataNodeLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,10 @@ export const dataNodeLogic = kea<dataNodeLogicType>([
if (cache.localResults[stringifiedQuery] && !refresh) {
return cache.localResults[stringifiedQuery]
}

if (!query.query) {
return null
}
}

if (!values.currentTeamId) {
Expand Down Expand Up @@ -337,6 +341,12 @@ export const dataNodeLogic = kea<dataNodeLogicType>([
],
})),
reducers(({ props }) => ({
isRefresh: [
false,
{
loadData: (_, { refresh }) => !!refresh,
},
],
dataLoading: [
false,
{
Expand Down Expand Up @@ -474,8 +484,12 @@ export const dataNodeLogic = kea<dataNodeLogicType>([
(variablesOverride) => !!variablesOverride,
],
isShowingCachedResults: [
() => [(_, props) => props.cachedResults ?? null, (_, props) => props.query],
(cachedResults: AnyResponseType | null, query: DataNode): boolean => {
(s) => [(_, props) => props.cachedResults ?? null, (_, props) => props.query, s.isRefresh],
(cachedResults: AnyResponseType | null, query: DataNode, isRefresh): boolean => {
if (isRefresh) {
return false
}

return (
!!cachedResults ||
(cache.localResults && 'query' in query && JSON.stringify(query.query) in cache.localResults)
Expand Down
188 changes: 119 additions & 69 deletions frontend/src/scenes/data-warehouse/editor/OutputPane.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
import 'react-data-grid/lib/styles.css'

import { IconGear } from '@posthog/icons'
import { LemonButton, LemonTabs, Spinner } from '@posthog/lemon-ui'
import { LemonButton, LemonTabs } from '@posthog/lemon-ui'
import clsx from 'clsx'
import { useActions, useValues } from 'kea'
import { AnimationType } from 'lib/animations/animations'
import { Animation } from 'lib/components/Animation/Animation'
import { ExportButton } from 'lib/components/ExportButton/ExportButton'
import { useMemo } from 'react'
import DataGrid from 'react-data-grid'
import { InsightErrorState } from 'scenes/insights/EmptyStates'
import { InsightErrorState, StatelessInsightLoadingState } from 'scenes/insights/EmptyStates'
import { HogQLBoldNumber } from 'scenes/insights/views/BoldNumber/BoldNumber'

import { KeyboardShortcut } from '~/layout/navigation-3000/components/KeyboardShortcut'
import { themeLogic } from '~/layout/navigation-3000/themeLogic'
import { dataNodeLogic } from '~/queries/nodes/DataNode/dataNodeLogic'
import { ElapsedTime } from '~/queries/nodes/DataNode/ElapsedTime'
import { LineGraph } from '~/queries/nodes/DataVisualization/Components/Charts/LineGraph'
import { SideBar } from '~/queries/nodes/DataVisualization/Components/SideBar'
import { Table } from '~/queries/nodes/DataVisualization/Components/Table'
Expand All @@ -39,12 +40,12 @@ export function OutputPane(): JSX.Element {
const { editingView, sourceQuery, exportContext, isValidView, error } = useValues(multitabEditorLogic)
const { saveAsInsight, saveAsView, setSourceQuery, runQuery } = useActions(multitabEditorLogic)
const { isDarkModeOn } = useValues(themeLogic)
const { response, responseLoading, responseError } = useValues(dataNodeLogic)
const { response, responseLoading, responseError, queryId, pollResponse } = useValues(dataNodeLogic)
const { dataWarehouseSavedQueriesLoading } = useValues(dataWarehouseViewsLogic)
const { updateDataWarehouseSavedQuery } = useActions(dataWarehouseViewsLogic)
const { visualizationType, queryCancelled } = useValues(dataVisualizationLogic)

const vizKey = `SQLEditorScene`
const vizKey = useMemo(() => `SQLEditorScene`, [])

const columns = useMemo(() => {
return (
Expand All @@ -69,70 +70,6 @@ export function OutputPane(): JSX.Element {
})
}, [response])

const ErrorState = useMemo((): JSX.Element | null => {
return (
<div className={clsx('flex-1 absolute top-0 left-0 right-0 bottom-0 overflow-scroll')}>
<InsightErrorState
query={sourceQuery}
excludeDetail
title={
queryCancelled
? 'The query was cancelled'
: response && 'error' in response
? (response as any).error
: responseError
}
/>
</div>
)
}, [responseError, sourceQuery, queryCancelled, response])

const Content = (): JSX.Element | null => {
if (activeTab === OutputTab.Results) {
if (responseError) {
return ErrorState
}

return responseLoading ? (
<Spinner className="text-3xl" />
) : !response ? (
<span className="text-muted mt-3">Query results will appear here</span>
) : (
<div className="flex-1 absolute top-0 left-0 right-0 bottom-0">
<DataGrid
className={isDarkModeOn ? 'rdg-dark h-full' : 'rdg-light h-full'}
columns={columns}
rows={rows}
/>
</div>
)
}

if (activeTab === OutputTab.Visualization) {
if (responseError) {
return ErrorState
}

return !response ? (
<span className="text-muted mt-3">Query be results will be visualized here</span>
) : (
<div className="flex-1 absolute top-0 left-0 right-0 bottom-0 px-4 py-1 hide-scrollbar">
<InternalDataTableVisualization
uniqueKey={vizKey}
query={sourceQuery}
setQuery={setSourceQuery}
context={{}}
cachedResults={undefined}
exportContext={exportContext}
onSaveInsight={saveAsInsight}
/>
</div>
)
}

return null
}

return (
<div className="flex flex-col w-full flex-1 bg-bg-3000">
{variablesForInsight.length > 0 && (
Expand Down Expand Up @@ -215,7 +152,26 @@ export function OutputPane(): JSX.Element {
</div>
</div>
<div className="flex flex-1 relative bg-dark justify-center items-center">
<Content />
<Content
activeTab={activeTab}
responseError={responseError}
responseLoading={responseLoading}
response={response}
sourceQuery={sourceQuery}
queryCancelled={queryCancelled}
columns={columns}
rows={rows}
isDarkModeOn={isDarkModeOn}
vizKey={vizKey}
setSourceQuery={setSourceQuery}
exportContext={exportContext}
saveAsInsight={saveAsInsight}
queryId={queryId}
pollResponse={pollResponse}
/>
</div>
<div className="flex justify-end pr-2 border-t">
<ElapsedTime />
</div>
</div>
)
Expand Down Expand Up @@ -303,3 +259,97 @@ function InternalDataTableVisualization(
</div>
)
}

const ErrorState = ({ responseError, sourceQuery, queryCancelled, response }: any): JSX.Element | null => {
return (
<div className={clsx('flex-1 absolute top-0 left-0 right-0 bottom-0 overflow-scroll')}>
<InsightErrorState
query={sourceQuery}
excludeDetail
title={
queryCancelled
? 'The query was cancelled'
: response && 'error' in response
? response.error
: responseError
}
/>
</div>
)
}

const Content = ({
activeTab,
responseError,
responseLoading,
response,
sourceQuery,
queryCancelled,
columns,
rows,
isDarkModeOn,
vizKey,
setSourceQuery,
exportContext,
saveAsInsight,
queryId,
pollResponse,
}: any): JSX.Element | null => {
if (activeTab === OutputTab.Results) {
if (responseError) {
return (
<ErrorState
responseError={responseError}
sourceQuery={sourceQuery}
queryCancelled={queryCancelled}
response={response}
/>
)
}

return responseLoading ? (
<StatelessInsightLoadingState queryId={queryId} pollResponse={pollResponse} />
) : !response ? (
<span className="text-muted mt-3">Query results will appear here</span>
) : (
<div className="flex-1 absolute top-0 left-0 right-0 bottom-0">
<DataGrid
className={isDarkModeOn ? 'rdg-dark h-full' : 'rdg-light h-full'}
columns={columns}
rows={rows}
/>
</div>
)
}

if (activeTab === OutputTab.Visualization) {
if (responseError) {
return (
<ErrorState
responseError={responseError}
sourceQuery={sourceQuery}
queryCancelled={queryCancelled}
response={response}
/>
)
}

return !response ? (
<span className="text-muted mt-3">Query be results will be visualized here</span>
) : (
<div className="flex-1 absolute top-0 left-0 right-0 bottom-0 px-4 py-1 hide-scrollbar">
<InternalDataTableVisualization
uniqueKey={vizKey}
query={sourceQuery}
setQuery={setSourceQuery}
context={{}}
cachedResults={undefined}
exportContext={exportContext}
onSaveInsight={saveAsInsight}
/>
</div>
)
}

return null
}
2 changes: 1 addition & 1 deletion frontend/src/scenes/data-warehouse/editor/QueryTabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ interface QueryTabsProps {

export function QueryTabs({ models, onClear, onClick, onAdd, activeModelUri }: QueryTabsProps): JSX.Element {
return (
<div className="flex flex-row overflow-scroll hide-scrollbar h-10">
<div className="flex flex-row w-full overflow-scroll hide-scrollbar h-10">
{models.map((model: QueryTab) => (
<QueryTabComponent
key={model.uri.path}
Expand Down
28 changes: 13 additions & 15 deletions frontend/src/scenes/data-warehouse/editor/QueryWindow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Monaco } from '@monaco-editor/react'
import { Spinner } from '@posthog/lemon-ui'
import { BindLogic, useActions, useValues } from 'kea'
import { router } from 'kea-router'
import type { editor as importedEditor } from 'monaco-editor'
Expand Down Expand Up @@ -40,14 +39,16 @@ export function QueryWindow(): JSX.Element {
const { selectTab, deleteTab, createTab, setQueryInput, runQuery, setError, setIsValidView } = useActions(logic)

return (
<div className="flex flex-1 flex-col h-full">
<QueryTabs
models={allTabs}
onClick={selectTab}
onClear={deleteTab}
onAdd={createTab}
activeModelUri={activeModelUri}
/>
<div className="flex flex-1 flex-col h-full overflow-hidden">
<div className="overflow-x-auto">
<QueryTabs
models={allTabs}
onClick={selectTab}
onClear={deleteTab}
onAdd={createTab}
activeModelUri={activeModelUri}
/>
</div>
{editingView && (
<div className="h-7 bg-warning-highlight p-1">
<span> Editing view "{editingView.name}"</span>
Expand Down Expand Up @@ -85,16 +86,13 @@ export function QueryWindow(): JSX.Element {
)
}

function InternalQueryWindow(): JSX.Element {
function InternalQueryWindow(): JSX.Element | null {
const { cacheLoading, sourceQuery, queryInput } = useValues(multitabEditorLogic)
const { setSourceQuery } = useActions(multitabEditorLogic)

// NOTE: hacky way to avoid flicker loading
if (cacheLoading) {
return (
<div className="flex-1 flex justify-center items-center">
<Spinner className="text-3xl" />
</div>
)
return null
}

const dataVisualizationLogicProps: DataVisualizationLogicProps = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const multitabEditorLogic = kea<multitabEditorLogicType>([
actions({
setQueryInput: (queryInput: string) => ({ queryInput }),
updateState: true,
runQuery: (queryOverride?: string) => ({ queryOverride }),
runQuery: (queryOverride?: string, switchTab?: boolean) => ({ queryOverride, switchTab }),
setActiveQuery: (query: string) => ({ query }),
setTabs: (tabs: QueryTab[]) => ({ tabs }),
addTab: (tab: QueryTab) => ({ tab }),
Expand Down Expand Up @@ -311,7 +311,7 @@ export const multitabEditorLogic = kea<multitabEditorLogicType>([
})
localStorage.setItem(editorModelsStateKey(props.key), JSON.stringify(queries))
},
runQuery: ({ queryOverride }) => {
runQuery: ({ queryOverride, switchTab }) => {
const query = queryOverride || values.queryInput

actions.setSourceQuery({
Expand All @@ -328,7 +328,7 @@ export const multitabEditorLogic = kea<multitabEditorLogicType>([
query,
},
autoLoad: false,
}).actions.loadData(true)
}).actions.loadData(!switchTab)
},
saveAsView: async () => {
LemonDialog.openForm({
Expand Down Expand Up @@ -418,7 +418,7 @@ export const multitabEditorLogic = kea<multitabEditorLogicType>([
const _model = props.monaco.editor.getModel(activeModelUri.uri)
const val = _model?.getValue()
actions.setQueryInput(val ?? '')
actions.runQuery()
actions.runQuery(undefined, true)
}
},
})),
Expand Down
Loading
Loading