update sql splitter

This commit is contained in:
Nate Kelley 2025-09-22 21:34:47 -06:00
parent 366f20577f
commit 87376682cb
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
3 changed files with 79 additions and 74 deletions

View File

@ -13,18 +13,35 @@ export interface AppVerticalCodeSplitterProps {
sql: string;
setSQL: (sql: string) => void;
runSQLError: string | undefined;
onRunQuery: () => Promise<void>;
data: DataResult;
fetchingData: boolean;
defaultLayout: LayoutSize;
initialLayout: LayoutSize | null;
autoSaveId: string;
topHidden?: boolean;
onSaveSQL?: () => Promise<void>;
disabledSave?: boolean;
gapAmount?: number;
className?: string;
readOnly?: boolean;
saveButton:
| false
| {
label?: string;
icon?: React.ReactNode;
loading?: boolean;
disabled?: boolean;
onClick: () => Promise<void>;
tooltip?: string;
};
runButton:
| false
| {
label?: string;
suffix?: React.ReactNode;
loading?: boolean;
disabled?: boolean;
onClick: () => Promise<void>;
tooltip?: string;
};
}
const MIN_LEFT_SIZE = 120;
@ -37,15 +54,14 @@ export const AppVerticalCodeSplitter = forwardRef<AppSplitterRef, AppVerticalCod
sql,
setSQL,
runSQLError,
onRunQuery,
onSaveSQL,
runButton,
saveButton,
data,
readOnly = false,
fetchingData,
defaultLayout,
initialLayout,
autoSaveId,
disabledSave = false,
topHidden = false,
gapAmount = 3,
className,
@ -79,13 +95,12 @@ export const AppVerticalCodeSplitter = forwardRef<AppSplitterRef, AppVerticalCod
sql={sql}
setDatasetSQL={setSQL}
error={runSQLError}
onRunQuery={onRunQuery}
onSaveSQL={onSaveSQL}
disabledSave={disabledSave}
runButton={runButton}
saveButton={saveButton}
readOnly={readOnly}
/>
),
[sql, setSQL, runSQLError, onRunQuery, onSaveSQL, disabledSave, readOnly]
[sql, setSQL, runSQLError, runButton, saveButton, readOnly]
)}
rightPanelClassName={dataContainerClassName}
rightChildren={useMemo(

View File

@ -5,31 +5,19 @@ import { ErrorClosableContainer } from '@/components/ui/error/ErrorClosableConta
import { Command, ReturnKey } from '@/components/ui/icons';
import { AppCodeEditor } from '@/components/ui/inputs/AppCodeEditor';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { AppTooltip } from '../../tooltip';
import type { AppVerticalCodeSplitterProps } from './AppVerticalCodeSplitter';
export const SQLContainer: React.FC<{
className?: string;
sql: string | undefined;
setDatasetSQL: (sql: string) => void;
onRunQuery: () => Promise<void>;
onSaveSQL: AppVerticalCodeSplitterProps['onSaveSQL'];
disabledSave?: AppVerticalCodeSplitterProps['disabledSave'];
error?: string | null;
readOnly?: boolean;
saveButton?: AppVerticalCodeSplitterProps['saveButton'];
runButton?: AppVerticalCodeSplitterProps['runButton'];
}> = React.memo(
({
disabledSave,
className = '',
readOnly = false,
sql,
setDatasetSQL,
onRunQuery,
onSaveSQL,
error,
}) => {
const [isRunning, setIsRunning] = useState(false);
const [isSaving, setIsSaving] = useState(false);
({ className = '', saveButton, readOnly = false, sql, setDatasetSQL, runButton, error }) => {
const { openInfoMessage } = useBusterNotifications();
const onCopySQL = () => {
@ -37,59 +25,40 @@ export const SQLContainer: React.FC<{
openInfoMessage('SQL copied to clipboard');
};
const onRunQueryPreflight = useMemoizedFn(async () => {
setIsRunning(true);
try {
await onRunQuery();
} catch (error) {
// Error handling is done by the parent component
console.error('Error running query:', error);
} finally {
setIsRunning(false);
}
});
const onSaveSQLPreflight = useMemoizedFn(async () => {
setIsSaving(true);
try {
await onSaveSQL?.();
} catch (error) {
// Error handling is done by the parent component
console.error('Error saving SQL:', error);
} finally {
setIsSaving(false);
}
});
const memoizedFooter = useMemo(() => {
return (
<>
<Button onClick={onCopySQL}>Copy SQL</Button>
<div className="flex items-center gap-2">
{onSaveSQL && !readOnly && (
<Button
disabled={disabledSave || !sql || isRunning}
variant="black"
loading={isSaving}
onClick={onSaveSQLPreflight}
>
Save
</Button>
{saveButton && !readOnly && (
<AppTooltip title={saveButton.tooltip} delayDuration={500}>
<Button
disabled={saveButton?.disabled || !sql}
variant="black"
loading={saveButton?.loading}
onClick={saveButton?.onClick}
prefix={saveButton.icon}
>
{saveButton.label || 'Save'}
</Button>
</AppTooltip>
)}
{!readOnly && (
{!readOnly && runButton && (
<Button
variant="default"
loading={isRunning}
loading={runButton?.loading}
disabled={!sql}
className="flex items-center space-x-0"
onClick={onRunQueryPreflight}
onClick={runButton?.onClick}
suffix={
<div className="flex items-center gap-x-1 text-sm">
<Command />
<ReturnKey />
</div>
runButton?.suffix || (
<div className="flex items-center gap-x-1 text-sm">
<Command />
<ReturnKey />
</div>
)
}
>
Run
@ -98,7 +67,7 @@ export const SQLContainer: React.FC<{
</div>
</>
);
}, [disabledSave, isRunning, sql, isSaving]);
}, [saveButton, runButton, sql]);
return (
<FileCard
@ -110,7 +79,7 @@ export const SQLContainer: React.FC<{
className="overflow-hidden border-x-0 border-t-0"
value={sql}
onChange={setDatasetSQL}
onMetaEnter={onRunQueryPreflight}
onMetaEnter={runButton ? runButton.onClick : undefined}
variant={null}
readOnly={readOnly}
/>

View File

@ -1,9 +1,12 @@
import type { DataResult } from '@buster/server-shared/metrics';
import React, { useCallback, useEffect, useState } from 'react';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import type { BusterMetric } from '@/api/asset_interfaces';
import { useGetMetric, useGetMetricData } from '@/api/buster_rest/metrics';
import type { AppSplitterRef, LayoutSize } from '@/components/ui/layouts/AppSplitter';
import { AppVerticalCodeSplitter } from '@/components/ui/layouts/AppVerticalCodeSplitter';
import {
AppVerticalCodeSplitter,
type AppVerticalCodeSplitterProps,
} from '@/components/ui/layouts/AppVerticalCodeSplitter';
import { useChatIsVersionHistoryMode } from '@/context/Chats/useIsVersionHistoryMode';
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
import { useMetricResultsLayout } from './useMetricResultsLayout';
@ -52,7 +55,7 @@ export const MetricViewSQLController: React.FC<{
const [sql, setSQL] = useState(metric?.sql || '');
const isSQLChanged = sql !== metric?.sql;
const disableSave = !sql || isRunningSQL || isSQLChanged;
const disableSave = !sql || isRunningSQL || !isSQLChanged;
const dataSourceId = metric?.data_source_id || '';
const data: DataResult | null = metricData?.dataFromRerun || metricData?.data || null;
@ -103,6 +106,25 @@ export const MetricViewSQLController: React.FC<{
}
}, [metric?.sql]);
const saveButton: AppVerticalCodeSplitterProps['saveButton'] = useMemo(() => {
return {
label: 'Save',
onClick: onSaveSQL,
loading: isSavingMetric,
disabled: disableSave,
tooltip: disableSave ? 'SQL is not changed' : 'Save SQL',
};
}, [onSaveSQL, isSavingMetric, disableSave]);
const runButton: AppVerticalCodeSplitterProps['runButton'] = useMemo(() => {
return {
label: 'Run',
onClick: onRunQuery,
loading: isRunningSQL,
disabled: false,
};
}, [onRunQuery, isRunningSQL]);
useViewSQLBlocker({ sql, originalSql: metric?.sql, enabled: isFetchedMetric, onResetToOriginal });
return (
@ -113,15 +135,14 @@ export const MetricViewSQLController: React.FC<{
sql={sql}
setSQL={setSQL}
runSQLError={runSQLError || saveMetricError}
onRunQuery={onRunQuery}
onSaveSQL={onSaveSQL}
data={data || []}
readOnly={isVersionHistoryMode}
disabledSave={disableSave}
fetchingData={isRunningSQL || isSavingMetric || !isFetchedInitialData}
defaultLayout={defaultLayout}
topHidden={false}
initialLayout={initialLayout}
saveButton={saveButton}
runButton={runButton}
/>
</div>
);