Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
--tanstack-first-column-header-bg,
var(--tanstack-table-header-cell-bg, var(--l2-background))
) !important;
padding-left: var(
--tanstack-cell-header-padding-left-first-column,
var(
--tanstack-cell-header-padding-left-override,
var(--tanstack-cell-padding-left, 0.3rem)
)
);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@
.tableHeaderCell {
padding: var(--tanstack-cell-padding-top) var(--tanstack-cell-padding-right)
var(--tanstack-cell-padding-bottom) var(--tanstack-cell-padding-left);
height: 36px;
height: var(--tanstack-table-header-height, 36px);
text-align: left;
font-size: 14px;
font-style: normal;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.container {
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
height: calc(100vh - 240px);
min-height: 400px;
}
7 changes: 7 additions & 0 deletions frontend/src/container/TracesExplorer/ListView/configs.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { TelemetryFieldKey } from 'api/v5/v5';
import { DEFAULT_PER_PAGE_OPTIONS } from 'hooks/queryPagination';

export const defaultSelectedColumns: string[] = [
Expand All @@ -10,3 +11,9 @@ export const defaultSelectedColumns: string[] = [
];

export const PER_PAGE_OPTIONS: number[] = [10, ...DEFAULT_PER_PAGE_OPTIONS];

// Pinned timestamp column
export const TIMESTAMP_FIELD = {
name: 'timestamp',
fieldContext: 'span',
} as TelemetryFieldKey;
124 changes: 49 additions & 75 deletions frontend/src/container/TracesExplorer/ListView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@ import {
import { useSelector } from 'react-redux';
import logEvent from 'api/common/logEvent';
import DownloadOptionsMenu from 'components/DownloadOptionsMenu/DownloadOptionsMenu';
import ErrorInPlace from 'components/ErrorInPlace/ErrorInPlace';
import ListViewOrderBy from 'components/OrderBy/ListViewOrderBy';
import { ResizeTable } from 'components/ResizeTable';
import type { TableColumnDef } from 'components/TanStackTableView/types';
import TracesTable from 'container/TracesExplorer/TracesTable/TracesTable';
import {
getFieldColumn,
TracesTableRow,
} from 'container/TracesExplorer/TracesTable/getFieldColumn';
import { ENTITY_VERSION_V5 } from 'constants/app';
import { LOCALSTORAGE } from 'constants/localStorage';
import { QueryParams } from 'constants/query';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch';
import NoLogs from 'container/NoLogs/NoLogs';
import { useOptionsMenu } from 'container/OptionsMenu';
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/types';
import TraceExplorerControls from 'container/TracesExplorer/Controls';
Expand All @@ -32,20 +34,22 @@ import { Pagination } from 'hooks/queryPagination';
import { getDefaultPaginationConfig } from 'hooks/queryPagination/utils';
import useUrlQueryData from 'hooks/useUrlQueryData';
import { ArrowUp10, Minus } from '@signozhq/icons';
import { useTimezone } from 'providers/Timezone';
import { AppState } from 'store/reducers';
import { Warning } from 'types/api';
import APIError from 'types/api/error';
import { DataSource } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';

import { TracesLoading } from '../TraceLoading/TraceLoading';
import { defaultSelectedColumns, PER_PAGE_OPTIONS } from './configs';
import { Container, tableStyles } from './styles';
import { getListColumns, transformDataWithDate } from './utils';
import {
defaultSelectedColumns,
PER_PAGE_OPTIONS,
TIMESTAMP_FIELD,
} from './configs';
import { getTraceLink, transformSpanRows } from './utils';

import './ListView.styles.scss';

import styles from './ListView.module.scss';

interface ListViewProps {
isFilterApplied: boolean;
setWarning: Dispatch<SetStateAction<Warning | undefined>>;
Expand Down Expand Up @@ -93,7 +97,7 @@ function ListView({
[stagedQuery, orderBy],
);

// TEMP — remove after traces moves to TanStack table.
// Stable sorted-name signature for the queryKey.
// - Drag updates selectColumns; raw queryKey would churn on reorder.
// - Trace API fetches only listed columns → add/remove must refetch.
// - Sorted-name signature: stable on reorder, changes on add/remove.
Expand Down Expand Up @@ -186,60 +190,42 @@ function ListView({
[queryTableDataResult],
);

const { formatTimezoneAdjustedTimestamp } = useTimezone();

const columns = useMemo(
() =>
getListColumns(
options?.selectColumns || [],
formatTimezoneAdjustedTimestamp,
const columns = useMemo<TableColumnDef<TracesTableRow>[]>(() => {
const fields = [
TIMESTAMP_FIELD,
...(options?.selectColumns ?? []).filter(
(field) => field.name !== TIMESTAMP_FIELD.name,
),
[options?.selectColumns, formatTimezoneAdjustedTimestamp],
);
];
return fields.map((field) => getFieldColumn(field));
}, [options?.selectColumns]);

const transformedQueryTableData = useMemo(
() => transformDataWithDate(queryTableData) || [],
const rows = useMemo(
() => transformSpanRows(queryTableData),
[queryTableData],
);

const handleDragColumn = useCallback(
(fromIndex: number, toIndex: number): void => {
const reordered = [...columns];
const [moved] = reordered.splice(fromIndex, 1);
reordered.splice(toIndex, 0, moved);
// `key` is the composite (fieldContext.name) — disambiguates same-name fields.
const orderedIds = reordered
.map((c) => String(c.key || ('dataIndex' in c && c.dataIndex) || ''))
.filter(Boolean);
config?.addColumn?.onReorder(orderedIds);
const handleColumnOrderChange = useCallback(
(cols: TableColumnDef<TracesTableRow>[]): void => {
config?.addColumn?.onReorder(cols.map((c) => c.id));
},
[columns, config],
[config],
);

const handleOrderChange = useCallback((value: string) => {
setOrderBy(value);
}, []);

const isDataAbsent =
!isLoading &&
!isFetching &&
!isError &&
transformedQueryTableData.length === 0;

useEffect(() => {
if (
!isLoading &&
!isFetching &&
!isError &&
transformedQueryTableData.length !== 0
) {
logEvent('Traces Explorer: Data present', {
if (!isLoading && !isFetching && !isError && rows.length !== 0) {
void logEvent('Traces Explorer: Data present', {
panelType,
});
}
}, [isLoading, isFetching, isError, transformedQueryTableData, panelType]);
}, [isLoading, isFetching, isError, rows, panelType]);

return (
<Container>
<div className={styles.container}>
<div className="trace-explorer-controls">
<div className="order-by-container">
<div className="order-by-label">
Expand All @@ -266,33 +252,21 @@ function ListView({
/>
</div>

{isError && error && <ErrorInPlace error={error as APIError} />}

{(isLoading || (isFetching && transformedQueryTableData.length === 0)) && (
<TracesLoading />
)}

{isDataAbsent && !isFilterApplied && (
<NoLogs dataSource={DataSource.TRACES} />
)}

{isDataAbsent && isFilterApplied && (
<EmptyLogsSearch dataSource={DataSource.TRACES} panelType="LIST" />
)}

{!isError && transformedQueryTableData.length !== 0 && (
<ResizeTable
tableLayout="fixed"
pagination={false}
scroll={{ x: 'max-content' }}
loading={isFetching}
style={tableStyles}
dataSource={transformedQueryTableData}
columns={columns}
onDragColumn={handleDragColumn}
/>
)}
</Container>
<TracesTable
data={rows}
columns={columns}
columnStorageKey={LOCALSTORAGE.TRACES_LIST_COLUMNS}
panelType="LIST"
getRowHref={getTraceLink}
isLoading={isLoading}
isFetching={isFetching}
isError={isError}
error={error}
isFilterApplied={isFilterApplied}
onColumnOrderChange={handleColumnOrderChange}
onColumnRemove={config?.addColumn?.onRemove}
/>
</div>
);
}

Expand Down
36 changes: 33 additions & 3 deletions frontend/src/container/TracesExplorer/ListView/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { TableColumnsType as ColumnsType } from 'antd';
import { Badge } from '@signozhq/ui/badge';
import { Typography } from '@signozhq/ui/typography';
import { TelemetryFieldKey } from 'api/v5/v5';
import type { TracesTableRow } from 'container/TracesExplorer/TracesTable/getFieldColumn';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import ROUTES from 'constants/routes';
import { buildCompositeKey } from 'container/OptionsMenu/utils';
Expand Down Expand Up @@ -41,12 +42,23 @@ export const transformDataWithDate = (
data[0]?.list?.map(({ data, timestamp }) => ({ ...data, date: timestamp })) ||
[];

export const getTraceLink = (record: RowData): string =>
`${ROUTES.TRACE}/${record.traceID || record.trace_id}${formUrlParams({
spanId: record.spanID || record.span_id,
export const getTraceLink = (record: Record<string, unknown>): string => {
function readId(value: unknown): string {
if (typeof value === 'string' || typeof value === 'number') {
return String(value);
}
return '';
}

const traceId = readId(record.traceID) || readId(record.trace_id);
const spanId = readId(record.spanID) || readId(record.span_id);

return `${ROUTES.TRACE}/${traceId}${formUrlParams({
spanId,
levelUp: 0,
levelDown: 0,
})}`;
};

export const getListColumns = (
selectedColumns: TelemetryFieldKey[],
Expand Down Expand Up @@ -136,3 +148,21 @@ export const getListColumns = (

return [...initialColumns, ...columns];
};

// Reshapes the query-range list payload into table rows. `id` mirrors span_id so
// TanStack sees genuine row changes on orderBy toggles instead of falling back to
// positional ids; `timestamp` is lifted from the wrapping ListItem.
export const transformSpanRows = (data: QueryDataV3[]): TracesTableRow[] => {
const list = data[0]?.list;
if (!list) {
return [];
}
return list.map((item) => {
const row = item.data as Record<string, unknown>;
return {
...row,
timestamp: item.timestamp,
id: row.span_id,
};
}) as TracesTableRow[];
};
62 changes: 62 additions & 0 deletions frontend/src/container/TracesExplorer/TracesTable/FieldCell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { Badge } from '@signozhq/ui/badge';
import TanStackTable from 'components/TanStackTableView';
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
import { getMs } from 'container/Trace/Filters/Panel/PanelBody/Duration/util';
import { useTimezone } from 'providers/Timezone';

import {
DURATION_FIELD_NAMES,
STATUS_FIELD_NAMES,
TIMESTAMP_FIELD_NAMES,
} from './constants';
import { stringifyCellValue } from './utils';

type FieldCellProps = {
name: string;
value: unknown;
};

function FieldCell({ name, value }: FieldCellProps): JSX.Element {
const { formatTimezoneAdjustedTimestamp } = useTimezone();

if (TIMESTAMP_FIELD_NAMES.has(name)) {
const ts = value as string | number;
const formatted =
typeof ts === 'string'
? formatTimezoneAdjustedTimestamp(ts, DATE_TIME_FORMATS.ISO_DATETIME_MS)
: formatTimezoneAdjustedTimestamp(
ts / 1e6,
DATE_TIME_FORMATS.ISO_DATETIME_MS,
);
const text = String(formatted);
return <TanStackTable.Text title={text}>{text}</TanStackTable.Text>;
}

if (value === '' || value == null) {
return <TanStackTable.Text data-testid={name}>-</TanStackTable.Text>;
}

const text = stringifyCellValue(value);

if (STATUS_FIELD_NAMES.has(name)) {
return (
<Badge data-testid={name} color="sakura" variant="outline">
{text}
</Badge>
);
}

if (DURATION_FIELD_NAMES.has(name)) {
return (
<TanStackTable.Text data-testid={name}>{getMs(text)}ms</TanStackTable.Text>
);
}

return (
<TanStackTable.Text data-testid={name} title={text}>
{text}
</TanStackTable.Text>
);
}

export default FieldCell;
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.tableWrapper {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
}

.tracesTable {
--tanstack-table-row-height: 54px;
--tanstack-table-header-height: 54px;

--tanstack-cell-padding-top-override: 5px;
--tanstack-cell-padding-bottom-override: 5px;
--tanstack-cell-padding-right-override: 15px;

--tanstack-cell-padding-left-override: 15px;
--tanstack-cell-header-padding-left-override: 5px;

--tanstack-cell-header-padding-left-first-column: 15px;

--tanstack-plain-body-line-clamp: 1;

--tanstack-table-cell-bg: var(--l2-background);
--tanstack-table-header-cell-bg: var(--l1-background-hover);
--tanstack-table-row-hover-bg: var(--l1-background-hover);
}
Loading
Loading