diff --git a/.changeset/bright-suns-prove.md b/.changeset/bright-suns-prove.md new file mode 100644 index 0000000000..1a57a46b4c --- /dev/null +++ b/.changeset/bright-suns-prove.md @@ -0,0 +1,5 @@ +--- +"@hyperdx/app": patch +--- + +Reusable DBSqlRowTableWithSideBar Component diff --git a/packages/app/src/ClickhousePage.tsx b/packages/app/src/ClickhousePage.tsx index 84fcca01f6..3fd81c93cb 100644 --- a/packages/app/src/ClickhousePage.tsx +++ b/packages/app/src/ClickhousePage.tsx @@ -7,6 +7,8 @@ import { useQueryStates, } from 'nuqs'; import { useForm } from 'react-hook-form'; +import { sql } from '@codemirror/lang-sql'; +import { format as formatSql } from '@hyperdx/common-utils/dist/sqlFormatter'; import { DisplayType } from '@hyperdx/common-utils/dist/types'; import { Box, @@ -19,6 +21,7 @@ import { Tabs, Text, } from '@mantine/core'; +import ReactCodeMirror from '@uiw/react-codemirror'; import { ConnectionSelectControlled } from '@/components/ConnectionSelect'; import { DBTimeChart } from '@/components/DBTimeChart'; @@ -702,9 +705,18 @@ function ClickhousePage() { Slowest Queries {}} - showExpandButton={false} + renderRowDetails={row => { + return ( + + ); + }} config={{ select: `event_time, query_kind, read_rows, @@ -735,7 +747,6 @@ function ClickhousePage() { ], limit: { limit: 100 }, }} - onScroll={() => {}} /> diff --git a/packages/app/src/DBDashboardPage.tsx b/packages/app/src/DBDashboardPage.tsx index 8ce561986f..ad656d8b6a 100644 --- a/packages/app/src/DBDashboardPage.tsx +++ b/packages/app/src/DBDashboardPage.tsx @@ -49,12 +49,10 @@ import { } from '@mantine/core'; import { useHover, usePrevious } from '@mantine/hooks'; import { notifications } from '@mantine/notifications'; -import { useQueryClient } from '@tanstack/react-query'; import { ContactSupportText } from '@/components/ContactSupportText'; import EditTimeChartForm from '@/components/DBEditTimeChartForm'; import DBNumberChart from '@/components/DBNumberChart'; -import { DBSqlRowTable } from '@/components/DBRowTable'; import DBTableChart from '@/components/DBTableChart'; import { DBTimeChart } from '@/components/DBTimeChart'; import { SQLInlineEditorControlled } from '@/components/SQLInlineEditor'; @@ -66,11 +64,10 @@ import { useDeleteDashboard, } from '@/dashboard'; -import DBRowSidePanel from './components/DBRowSidePanel'; +import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar'; import OnboardingModal from './components/OnboardingModal'; import { Tags } from './components/Tags'; import { useDashboardRefresh } from './hooks/useDashboardRefresh'; -import { useAllFields } from './hooks/useMetadata'; import api from './api'; import { DEFAULT_CHART_CONFIG } from './ChartUtils'; import { IS_LOCAL_MODE } from './config'; @@ -89,7 +86,7 @@ import { import { parseTimeQuery, useNewTimeQuery } from './timeQuery'; import { useConfirm } from './useConfirm'; import { getMetricTableName, hashCode, omit } from './utils'; -import { ZIndexContext } from './zIndex'; +import { useZIndex, ZIndexContext } from './zIndex'; import 'react-grid-layout/css/styles.css'; import 'react-resizable/css/styles.css'; @@ -197,17 +194,6 @@ const Tile = forwardRef( const [hovered, setHovered] = useState(false); - // Search tile - const [rowId, setRowId] = useQueryState('rowWhere'); - const [_, setRowSource] = useQueryState('rowSource'); - const handleRowExpandClick = useCallback( - (rowWhere: string) => { - setRowId(rowWhere); - setRowSource(chart.config.source); - }, - [chart.config.source, setRowId, setRowSource], - ); - const alert = chart.config.alert; const alertIndicatorColor = useMemo(() => { if (!alert) { @@ -351,7 +337,7 @@ const Tile = forwardRef( )} {queriedConfig?.displayType === DisplayType.Search && ( - {}} isLive={false} queryKeyPrefix={'search'} /> @@ -402,6 +385,8 @@ const EditTileModal = ({ isSaving?: boolean; onSave: (chart: Tile) => void; }) => { + const contextZIndex = useZIndex(); + const modalZIndex = contextZIndex + 10; return ( {chart != null && ( - {}} - dateRange={dateRange} - isSaving={isSaving} - onSave={config => { - onSave({ - ...chart, - config: config, - }); - }} - onClose={onClose} - /> + + {}} + dateRange={dateRange} + isSaving={isSaving} + onSave={config => { + onSave({ + ...chart, + config: config, + }); + }} + onClose={onClose} + /> + )} ); @@ -1068,13 +1056,6 @@ function DBDashboardPage({ presetConfig }: { presetConfig?: Dashboard }) { > + Add New Tile - {rowId && rowSidePanelSource && ( - - )} ); } diff --git a/packages/app/src/DBSearchPage.tsx b/packages/app/src/DBSearchPage.tsx index 3e41563bd9..deb2196e11 100644 --- a/packages/app/src/DBSearchPage.tsx +++ b/packages/app/src/DBSearchPage.tsx @@ -58,13 +58,9 @@ import { notifications } from '@mantine/notifications'; import { useIsFetching } from '@tanstack/react-query'; import CodeMirror from '@uiw/react-codemirror'; -import { useTimeChartSettings } from '@/ChartUtils'; import { ContactSupportText } from '@/components/ContactSupportText'; import DBDeltaChart from '@/components/DBDeltaChart'; import DBHeatmapChart from '@/components/DBHeatmapChart'; -import DBRowSidePanel from '@/components/DBRowSidePanel'; -import { RowSidePanelContext } from '@/components/DBRowSidePanel'; -import { DBSqlRowTable } from '@/components/DBRowTable'; import { DBSearchPageFilters } from '@/components/DBSearchPageFilters'; import { DBTimeChart } from '@/components/DBTimeChart'; import { ErrorBoundary } from '@/components/ErrorBoundary'; @@ -103,6 +99,7 @@ import { parseTimeQuery, useNewTimeQuery } from '@/timeQuery'; import { QUERY_LOCAL_STORAGE, useLocalStorage, usePrevious } from '@/utils'; import { SQLPreview } from './components/ChartSQLPreview'; +import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar'; import PatternTable from './components/PatternTable'; import { useTableMetadata } from './hooks/useMetadata'; import { useSqlSuggestions } from './hooks/useSqlSuggestions'; @@ -696,7 +693,6 @@ function DBSearchPage() { const inputSourceObj = inputSourceObjs?.find(s => s.id === inputSource); const defaultOrderBy = useDefaultOrderBy(inputSource); - const [rowId, setRowId] = useQueryState('rowWhere'); const [displayedTimeInputValue, setDisplayedTimeInputValue] = useState('Live Tail'); @@ -880,13 +876,9 @@ function DBSearchPage() { [isLive, setIsLive], ); - const onRowExpandClick = useCallback( - (rowWhere: string) => { - setIsLive(false); - setRowId(rowWhere); - }, - [setRowId, setIsLive], - ); + const onSidebarOpen = useCallback(() => { + setIsLive(false); + }, [setIsLive]); const [modelFormExpanded, setModelFormExpanded] = useState(false); // Used in local mode const [saveSearchModalState, setSaveSearchModalState] = useState< @@ -1010,7 +1002,7 @@ function DBSearchPage() { const { data: me } = api.useMe(); // Callback to handle when rows are expanded - kick user out of live tail - const handleExpandedRowsChange = useCallback( + const onExpandedRowsChange = useCallback( (hasExpandedRows: boolean) => { if (hasExpandedRows && isLive) { setIsLive(false); @@ -1472,25 +1464,6 @@ function DBSearchPage() { - - {searchedSource && ( - setRowId(null)} - /> - )} - {searchedConfig != null && searchedSource != null && ( )} {chartConfig && + searchedConfig.source && dbSqlRowTableConfig && analysisMode === 'results' && ( - - - + config={dbSqlRowTableConfig} + sourceId={searchedConfig.source} + onSidebarOpen={onSidebarOpen} + onExpandedRowsChange={onExpandedRowsChange} + enabled={isReady} + isLive={isLive ?? true} + queryKeyPrefix={QUERY_KEY_PREFIX} + onScroll={onTableScroll} + onError={handleTableError} + denoiseResults={denoiseResults} + collapseAllRows={collapseAllRows} + /> )} )} diff --git a/packages/app/src/KubernetesDashboardPage.tsx b/packages/app/src/KubernetesDashboardPage.tsx index 7c0e2612ea..94aaf50b1f 100644 --- a/packages/app/src/KubernetesDashboardPage.tsx +++ b/packages/app/src/KubernetesDashboardPage.tsx @@ -38,8 +38,7 @@ import { import { TimePicker } from '@/components/TimePicker'; import { ConnectionSelectControlled } from './components/ConnectionSelect'; -import DBRowSidePanel from './components/DBRowSidePanel'; -import { DBSqlRowTable } from './components/DBRowTable'; +import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar'; import { DBTimeChart } from './components/DBTimeChart'; import { FormatPodStatus } from './components/KubeComponents'; import { KubernetesFilters } from './components/KubernetesFilters'; @@ -830,24 +829,6 @@ function KubernetesDashboardPage() { [_searchQuery, setSearchQuery], ); - // Row details side panel - const [rowId, setRowId] = useQueryState('rowWhere'); - const [rowSource, setRowSource] = useQueryState('rowSource'); - const { data: rowSidePanelSource } = useSource({ id: rowSource || '' }); - - const handleSidePanelClose = React.useCallback(() => { - setRowId(null); - setRowSource(null); - }, [setRowId, setRowSource]); - - const handleRowExpandClick = React.useCallback( - (rowWhere: string) => { - setRowId(rowWhere); - setRowSource(logSource?.id ?? null); - }, - [logSource?.id, setRowId, setRowSource], - ); - return ( @@ -869,13 +850,6 @@ function KubernetesDashboardPage() { logSource={logSource} /> )} - {rowId && rowSidePanelSource && ( - - )} @@ -1047,7 +1021,7 @@ function KubernetesDashboardPage() { {logSource && ( - {}} /> )} diff --git a/packages/app/src/NamespaceDetailsSidePanel.tsx b/packages/app/src/NamespaceDetailsSidePanel.tsx index ace7e01849..b43c0020b7 100644 --- a/packages/app/src/NamespaceDetailsSidePanel.tsx +++ b/packages/app/src/NamespaceDetailsSidePanel.tsx @@ -22,7 +22,6 @@ import { K8S_CPU_PERCENTAGE_NUMBER_FORMAT, K8S_MEM_NUMBER_FORMAT, } from '@/ChartUtils'; -import { DBSqlRowTable } from '@/components/DBRowTable'; import { DBTimeChart } from '@/components/DBTimeChart'; import { DrawerBody, DrawerHeader } from '@/components/DrawerUtils'; import { useQueriedChartConfig } from '@/hooks/useChartConfig'; @@ -31,6 +30,7 @@ import { getEventBody } from '@/source'; import { parseTimeQuery, useTimeQuery } from '@/timeQuery'; import { useZIndex, ZIndexContext } from '@/zIndex'; +import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar'; import { useGetKeyValues, useTableMetadata } from './hooks/useMetadata'; import styles from '../styles/LogSidePanel.module.scss'; @@ -182,7 +182,10 @@ function NamespaceLogs({ - {}} - highlightedLineId={undefined} isLive={false} queryKeyPrefix="k8s-dashboard-namespace-logs" - onScroll={() => {}} - showExpandButton={false} /> diff --git a/packages/app/src/NodeDetailsSidePanel.tsx b/packages/app/src/NodeDetailsSidePanel.tsx index 9fa2288a3d..5d108a1216 100644 --- a/packages/app/src/NodeDetailsSidePanel.tsx +++ b/packages/app/src/NodeDetailsSidePanel.tsx @@ -25,7 +25,6 @@ import { K8S_CPU_PERCENTAGE_NUMBER_FORMAT, K8S_MEM_NUMBER_FORMAT, } from '@/ChartUtils'; -import { DBSqlRowTable } from '@/components/DBRowTable'; import { DBTimeChart } from '@/components/DBTimeChart'; import { DrawerBody, DrawerHeader } from '@/components/DrawerUtils'; import { InfraPodsStatusTable } from '@/KubernetesDashboardPage'; @@ -34,6 +33,7 @@ import { parseTimeQuery, useTimeQuery } from '@/timeQuery'; import { formatUptime } from '@/utils'; import { useZIndex, ZIndexContext } from '@/zIndex'; +import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar'; import { useQueriedChartConfig } from './hooks/useChartConfig'; import { useGetKeyValues, useTableMetadata } from './hooks/useMetadata'; @@ -201,7 +201,10 @@ function NodeLogs({ - {}} - highlightedLineId={undefined} isLive={false} queryKeyPrefix="k8s-dashboard-node-logs" - showExpandButton={false} - onScroll={() => {}} /> diff --git a/packages/app/src/PodDetailsSidePanel.tsx b/packages/app/src/PodDetailsSidePanel.tsx index 60eac8a6ad..9608c21b8c 100644 --- a/packages/app/src/PodDetailsSidePanel.tsx +++ b/packages/app/src/PodDetailsSidePanel.tsx @@ -22,13 +22,13 @@ import { K8S_MEM_NUMBER_FORMAT, } from '@/ChartUtils'; import DBRowSidePanel from '@/components/DBRowSidePanel'; -import { DBSqlRowTable } from '@/components/DBRowTable'; import { DBTimeChart } from '@/components/DBTimeChart'; import { DrawerBody, DrawerHeader } from '@/components/DrawerUtils'; import { KubeTimeline, useV2LogBatch } from '@/components/KubeComponents'; import { parseTimeQuery, useTimeQuery } from '@/timeQuery'; import { useZIndex, ZIndexContext } from '@/zIndex'; +import DBSqlRowTableWithSideBar from './components/DBSqlRowTableWithSidebar'; import { useGetKeyValues, useTableMetadata } from './hooks/useMetadata'; import { getEventBody } from './source'; @@ -206,14 +206,13 @@ function PodLogs({ - {}} /> diff --git a/packages/app/src/components/ContextSidePanel.tsx b/packages/app/src/components/ContextSidePanel.tsx index b8b39bb502..2127435059 100644 --- a/packages/app/src/components/ContextSidePanel.tsx +++ b/packages/app/src/components/ContextSidePanel.tsx @@ -319,10 +319,11 @@ export default function ContextSubpanel({ diff --git a/packages/app/src/components/DBEditTimeChartForm.tsx b/packages/app/src/components/DBEditTimeChartForm.tsx index 133c1b305e..565a416c5b 100644 --- a/packages/app/src/components/DBEditTimeChartForm.tsx +++ b/packages/app/src/components/DBEditTimeChartForm.tsx @@ -50,7 +50,6 @@ import { import { AGG_FNS } from '@/ChartUtils'; import { AlertChannelForm, getAlertReferenceLines } from '@/components/Alerts'; import ChartSQLPreview from '@/components/ChartSQLPreview'; -import { DBSqlRowTable } from '@/components/DBRowTable'; import DBTableChart from '@/components/DBTableChart'; import { DBTimeChart } from '@/components/DBTimeChart'; import { SQLInlineEditorControlled } from '@/components/SQLInlineEditor'; @@ -75,6 +74,7 @@ import HDXMarkdownChart from '../HDXMarkdownChart'; import { AggFnSelectControlled } from './AggFnSelect'; import DBNumberChart from './DBNumberChart'; +import DBSqlRowTableWithSideBar from './DBSqlRowTableWithSidebar'; import { CheckBoxControlled, InputControlled, @@ -979,7 +979,8 @@ export default function EditTimeChartForm({ className="flex-grow-1 d-flex flex-column" style={{ height: 400 }} > - {}} - highlightedLineId={undefined} enabled isLive={false} - showExpandButton={false} queryKeyPrefix={'search'} - onScroll={() => {}} /> )} @@ -1029,13 +1026,12 @@ export default function EditTimeChartForm({ className="flex-grow-1 d-flex flex-column" style={{ height: 400 }} > - )} diff --git a/packages/app/src/components/DBRowSidePanel.tsx b/packages/app/src/components/DBRowSidePanel.tsx index adf21db597..c12a895cf1 100644 --- a/packages/app/src/components/DBRowSidePanel.tsx +++ b/packages/app/src/components/DBRowSidePanel.tsx @@ -15,7 +15,7 @@ import { useHotkeys } from 'react-hotkeys-hook'; import Drawer from 'react-modern-drawer'; import { TSource } from '@hyperdx/common-utils/dist/types'; import { ChartConfigWithDateRange } from '@hyperdx/common-utils/dist/types'; -import { Box, Stack } from '@mantine/core'; +import { Box, OptionalPortal, Stack } from '@mantine/core'; import { useClickOutside } from '@mantine/hooks'; import DBRowSidePanelHeader, { @@ -39,7 +39,7 @@ import DBTracePanel from './DBTracePanel'; import 'react-modern-drawer/dist/index.css'; import styles from '@/../styles/LogSidePanel.module.scss'; -export const RowSidePanelContext = createContext<{ +export type RowSidePanelContextProps = { onPropertyAddClick?: (keyPath: string, value: string) => void; generateSearchUrl?: ({ where, @@ -59,7 +59,9 @@ export const RowSidePanelContext = createContext<{ dbSqlRowTableConfig?: ChartConfigWithDateRange; isChildModalOpen?: boolean; setChildModalOpen?: (open: boolean) => void; -}>({}); +}; + +export const RowSidePanelContext = createContext({}); enum Tab { Overview = 'overview', @@ -148,7 +150,7 @@ const DBRowSidePanel = ({ : Tab.Parsed; const [queryTab, setQueryTab] = useQueryState( - 'tab', + 'sidePanelTab', parseAsStringEnum(Object.values(Tab)).withDefault(defaultTab), ); @@ -498,50 +500,52 @@ export default function DBRowSidePanelErrorBoundary({ }, ['mouseup', 'touchend']); return ( - { - if (!subDrawerOpen) { - _onClose(); - } - }} - direction="right" - size={`${width}vw`} - zIndex={drawerZIndex} - enableOverlay={subDrawerOpen} - > - -
- - - ( - -
- An error occurred while rendering this event. -
- -
- {error?.error?.message} -
-
- )} - > - -
-
-
-
+ + { + if (!subDrawerOpen) { + _onClose(); + } + }} + direction="right" + size={`${width}vw`} + zIndex={drawerZIndex} + enableOverlay={subDrawerOpen} + > + +
+ + + ( + +
+ An error occurred while rendering this event. +
+ +
+ {error?.error?.message} +
+
+ )} + > + +
+
+
+
+
); } diff --git a/packages/app/src/components/DBRowTable.tsx b/packages/app/src/components/DBRowTable.tsx index 83411fa0cc..de5abc5a28 100644 --- a/packages/app/src/components/DBRowTable.tsx +++ b/packages/app/src/components/DBRowTable.tsx @@ -281,7 +281,7 @@ export const RawLogTable = memo( generateRowId, onInstructionsClick, // onPropertySearchClick, - onRowExpandClick, + onRowDetailsClick, onScroll, onSettingsClick, onShowPatternsClick, @@ -296,6 +296,7 @@ export const RawLogTable = memo( loadingDate, config, onChildModalOpen, + renderRowDetails, source, onExpandedRowsChange, collapseAllRows, @@ -306,16 +307,16 @@ export const RawLogTable = memo( onSettingsClick?: () => void; onInstructionsClick?: () => void; rows: Record[]; - isLoading: boolean; - fetchNextPage: (options?: FetchNextPageOptions | undefined) => any; - onRowExpandClick: (row: Record) => void; + isLoading?: boolean; + fetchNextPage?: (options?: FetchNextPageOptions | undefined) => any; + onRowDetailsClick: (row: Record) => void; generateRowId: (row: Record) => string; // onPropertySearchClick: ( // name: string, // value: string | number | boolean, // ) => void; - hasNextPage: boolean; - highlightedLineId: string | undefined; + hasNextPage?: boolean; + highlightedLineId?: string; onScroll?: (scrollTop: number) => void; isLive: boolean; onShowPatternsClick?: () => void; @@ -335,6 +336,7 @@ export const RawLogTable = memo( onExpandedRowsChange?: (hasExpandedRows: boolean) => void; collapseAllRows?: boolean; showExpandButton?: boolean; + renderRowDetails?: (row: Record) => React.ReactNode; }) => { const generateRowMatcher = generateRowId; @@ -359,9 +361,9 @@ export const RawLogTable = memo( const _onRowExpandClick = useCallback( ({ __hyperdx_id, ...row }: Record) => { - onRowExpandClick(row); + onRowDetailsClick?.(row); }, - [onRowExpandClick], + [onRowDetailsClick], ); const { width } = useWindowSize(); @@ -514,7 +516,7 @@ export const RawLogTable = memo( hasNextPage ) { // Cancel refetch is important to ensure we wait for the last fetch to finish - fetchNextPage({ cancelRefetch: false }); + fetchNextPage?.({ cancelRefetch: false }); } } }, @@ -604,7 +606,7 @@ export const RawLogTable = memo( useEffect(() => { if ( scrolledToHighlightedLine || - highlightedLineId == null || + !highlightedLineId || rowVirtualizer == null ) { return; @@ -613,13 +615,13 @@ export const RawLogTable = memo( const rowIdx = dedupedRows.findIndex( l => getRowId(l) === highlightedLineId, ); - if (rowIdx == -1) { + if (rowIdx == -1 && highlightedLineId) { if ( dedupedRows.length < MAX_SCROLL_FETCH_LINES && !isLoading && hasNextPage ) { - fetchNextPage({ cancelRefetch: false }); + fetchNextPage?.({ cancelRefetch: false }); } } else { setScrolledToHighlightedLine(true); @@ -825,7 +827,8 @@ export const RawLogTable = memo( + > + {renderRowDetails?.({ + id: rowId, + ...row.original, + })} + )} ); @@ -1124,7 +1132,7 @@ function DBSqlRowTableComponent({ config, sourceId, onError, - onRowExpandClick, + onRowDetailsClick, highlightedLineId, enabled = true, isLive = false, @@ -1135,14 +1143,16 @@ function DBSqlRowTableComponent({ onExpandedRowsChange, collapseAllRows, showExpandButton = true, + renderRowDetails, }: { config: ChartConfigWithDateRange; sourceId?: string; - onRowExpandClick?: (where: string) => void; - highlightedLineId: string | undefined; + onRowDetailsClick?: (where: string) => void; + highlightedLineId?: string; queryKeyPrefix?: string; enabled?: boolean; isLive?: boolean; + renderRowDetails?: (r: { [key: string]: unknown }) => React.ReactNode; onScroll?: (scrollTop: number) => void; onError?: (error: Error | ClickHouseQueryError) => void; denoiseResults?: boolean; @@ -1213,11 +1223,11 @@ function DBSqlRowTableComponent({ const getRowWhere = useRowWhere({ meta: data?.meta, aliasMap }); - const _onRowExpandClick = useCallback( + const _onRowDetailsClick = useCallback( (row: Record) => { - return onRowExpandClick?.(getRowWhere(row)); + return onRowDetailsClick?.(getRowWhere(row)); }, - [onRowExpandClick, getRowWhere], + [onRowDetailsClick, getRowWhere], ); useEffect(() => { @@ -1331,11 +1341,12 @@ function DBSqlRowTableComponent({ displayedColumns={columns} highlightedLineId={highlightedLineId} rows={denoiseResults ? (denoisedRows?.data ?? []) : processedRows} + renderRowDetails={renderRowDetails} isLoading={isLoading} fetchNextPage={fetchNextPage} // onPropertySearchClick={onPropertySearchClick} hasNextPage={hasNextPage} - onRowExpandClick={_onRowExpandClick} + onRowDetailsClick={_onRowDetailsClick} onScroll={onScroll} generateRowId={getRowWhere} isError={isError} diff --git a/packages/app/src/components/DBSqlRowTableWithSidebar.tsx b/packages/app/src/components/DBSqlRowTableWithSidebar.tsx new file mode 100644 index 0000000000..e254b0fb71 --- /dev/null +++ b/packages/app/src/components/DBSqlRowTableWithSidebar.tsx @@ -0,0 +1,162 @@ +import { useCallback } from 'react'; +import { useQueryState } from 'nuqs'; +import { ClickHouseQueryError } from '@hyperdx/common-utils/dist/clickhouse'; +import { + ChartConfigWithDateRange, + TSource, +} from '@hyperdx/common-utils/dist/types'; + +import { useSource } from '@/source'; +import TabBar from '@/TabBar'; +import { useLocalStorage } from '@/utils'; + +import { RowDataPanel } from './DBRowDataPanel'; +import { RowOverviewPanel } from './DBRowOverviewPanel'; +import DBRowSidePanel, { + RowSidePanelContext, + RowSidePanelContextProps, +} from './DBRowSidePanel'; +import { BreadcrumbEntry } from './DBRowSidePanelHeader'; +import { DBSqlRowTable } from './DBRowTable'; + +interface Props { + sourceId: string; + config: ChartConfigWithDateRange; + onError?: (error: Error | ClickHouseQueryError) => void; + onScroll?: (scrollTop: number) => void; + onSidebarOpen?: (rowId: string) => void; + onExpandedRowsChange?: (hasExpandedRows: boolean) => void; + onPropertyAddClick?: (keyPath: string, value: string) => void; + context?: RowSidePanelContextProps; + enabled?: boolean; + isLive?: boolean; + queryKeyPrefix?: string; + denoiseResults?: boolean; + collapseAllRows?: boolean; + isNestedPanel?: boolean; + breadcrumbPath?: BreadcrumbEntry[]; +} + +export default function DBSqlRowTableWithSideBar({ + sourceId, + config, + onError, + onScroll, + context, + onExpandedRowsChange, + denoiseResults, + collapseAllRows, + isLive, + enabled, + isNestedPanel, + breadcrumbPath, + onSidebarOpen, +}: Props) { + const { data: sourceData } = useSource({ id: sourceId }); + const [rowId, setRowId] = useQueryState('rowWhere'); + const [, setRowSource] = useQueryState('rowSource'); + + const onOpenSidebar = useCallback( + (rowWhere: string) => { + setRowId(rowWhere); + setRowSource(sourceId); + onSidebarOpen?.(rowWhere); + }, + [setRowId, setRowSource, sourceId, onSidebarOpen], + ); + + const onCloseSidebar = useCallback(() => { + setRowId(null); + setRowSource(null); + }, [setRowId, setRowSource]); + + return ( + + {sourceData && ( + + )} + { + if (!sourceData) { + return
Loading...
; + } + return ( + + ); + }} + onScroll={onScroll} + onError={onError} + onExpandedRowsChange={onExpandedRowsChange} + collapseAllRows={collapseAllRows} + /> +
+ ); +} + +enum InlineTab { + Overview = 'overview', + ColumnValues = 'columnValues', +} + +function RowOverviewPanelWrapper({ + source, + rowId, +}: { + source: TSource; + rowId: string; +}) { + // Use localStorage to persist the selected tab + const [activeTab, setActiveTab] = useLocalStorage( + 'hdx-expanded-row-default-tab', + InlineTab.ColumnValues, + ); + + return ( +
+
+ +
+
+ {activeTab === InlineTab.Overview && ( +
+ +
+ )} + {activeTab === InlineTab.ColumnValues && ( + + )} +
+
+ ); +} diff --git a/packages/app/src/components/ExpandableRowTable.tsx b/packages/app/src/components/ExpandableRowTable.tsx index 4b148190cc..98cfda4d6e 100644 --- a/packages/app/src/components/ExpandableRowTable.tsx +++ b/packages/app/src/components/ExpandableRowTable.tsx @@ -4,29 +4,17 @@ import { useQueryState } from 'nuqs'; import { TSource } from '@hyperdx/common-utils/dist/types'; import { IconChevronRight } from '@tabler/icons-react'; -import { useLocalStorage } from '@/utils'; - -import TabBar from '../TabBar'; - -import { RowDataPanel } from './DBRowDataPanel'; -import { RowOverviewPanel } from './DBRowOverviewPanel'; - import styles from '../../styles/LogTable.module.scss'; -enum InlineTab { - Overview = 'overview', - ColumnValues = 'columnValues', -} - // Hook that provides a function to open the sidebar with specific row details const useSidebarOpener = () => { const [, setRowId] = useQueryState('rowWhere'); const [, setRowSource] = useQueryState('rowSource'); return useCallback( - (rowWhere: string, sourceId: string) => { + (rowWhere: string, sourceId?: string) => { setRowId(rowWhere); - setRowSource(sourceId); + setRowSource(sourceId ?? null); }, [setRowId, setRowSource], ); @@ -35,27 +23,23 @@ const useSidebarOpener = () => { export const ExpandedLogRow = memo( ({ columnsLength, + children, virtualKey, source, rowId, measureElement, virtualIndex, }: { + children: React.ReactNode; columnsLength: number; virtualKey: string; - source: TSource | undefined; + source?: TSource; rowId: string; measureElement?: (element: HTMLElement | null) => void; virtualIndex?: number; }) => { const openSidebar = useSidebarOpener(); - // Use localStorage to persist the selected tab - const [activeTab, setActiveTab] = useLocalStorage( - 'hdx-expanded-row-default-tab', - InlineTab.ColumnValues, - ); - return (
- {source ? ( - <> -
-
- {openSidebar && ( - - )} - -
-
- {activeTab === InlineTab.Overview && ( -
- -
- )} - {activeTab === InlineTab.ColumnValues && ( - +
+
+ {openSidebar && ( +
- - ) : ( -
Loading...
- )} + onClick={() => openSidebar(rowId, source?.id)} + title="Open in sidebar" + aria-label="Open in sidebar" + style={{ + zIndex: 1, + fontSize: '12px', + lineHeight: 1, + }} + > + + + )} + {children} +
+
diff --git a/packages/app/src/components/PatternSidePanel.tsx b/packages/app/src/components/PatternSidePanel.tsx index bcdf607a87..4e390420e1 100644 --- a/packages/app/src/components/PatternSidePanel.tsx +++ b/packages/app/src/components/PatternSidePanel.tsx @@ -126,12 +126,9 @@ export default function PatternSidePanel({ displayedColumns={displayedColumns} columnTypeMap={columnTypeMap} columnNameMap={columnNameMap} - isLoading={false} - fetchNextPage={() => {}} - onRowExpandClick={handleRowClick} + onRowDetailsClick={handleRowClick} wrapLines={false} - highlightedLineId={''} - hasNextPage={false} + showExpandButton={false} isLive={false} /> @@ -143,6 +140,7 @@ export default function PatternSidePanel({ rowId={selectedRowWhere} onClose={handleCloseRowSidePanel} isNestedPanel={true} + breadcrumbPath={[{ label: 'Pattern Overview' }]} /> )}
diff --git a/packages/app/src/components/PatternTable.tsx b/packages/app/src/components/PatternTable.tsx index a73b1c2b3d..6c82996d2b 100644 --- a/packages/app/src/components/PatternTable.tsx +++ b/packages/app/src/components/PatternTable.tsx @@ -67,7 +67,7 @@ export default function PatternTable({ 'severityText', 'pattern', ]} - onRowExpandClick={row => setSelectedPattern(row as Pattern)} + onRowDetailsClick={row => setSelectedPattern(row as Pattern)} hasNextPage={false} fetchNextPage={() => {}} highlightedLineId={''} @@ -80,6 +80,7 @@ export default function PatternTable({ severityText: 'level', }} config={patternQueryConfig} + showExpandButton={false} /> {selectedPattern && source && ( { - setRowId(null); - setRowSource(null); - }, [setRowId, setRowSource]); - - const handleRowExpandClick = useCallback( - (rowWhere: string) => { - setRowId(rowWhere); - setRowSource(source.id); - }, - [source.id, setRowId, setRowSource], - ); - const { data, isLoading, isError, error } = useQueriedChartConfig( { ...source, @@ -130,7 +109,9 @@ export default function SlowestEventsTile({ ) : ( source && ( <> - {}} /> - {rowId && rowSidePanelSource && ( - - )} ) )}