mirror of
https://github.com/SigNoz/signoz.git
synced 2025-12-17 15:36:48 +00:00
Merge branch 'main' into improveTraceQuery
This commit is contained in:
commit
334b717f23
@ -26,7 +26,7 @@ const config: Config.InitialOptions = {
|
|||||||
'^.+\\.(js|jsx)$': 'babel-jest',
|
'^.+\\.(js|jsx)$': 'babel-jest',
|
||||||
},
|
},
|
||||||
transformIgnorePatterns: [
|
transformIgnorePatterns: [
|
||||||
'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@signozhq/design-tokens|@signozhq/calendar|@signozhq/input|@signozhq/popover|@signozhq/button|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn)/)',
|
'node_modules/(?!(lodash-es|react-dnd|core-dnd|@react-dnd|dnd-core|react-dnd-html5-backend|axios|@signozhq/design-tokens|@signozhq/table|@signozhq/calendar|@signozhq/input|@signozhq/popover|@signozhq/button|@signozhq/sonner|@signozhq/*|date-fns|d3-interpolate|d3-color|api|@codemirror|@lezer|@marijn)/)',
|
||||||
],
|
],
|
||||||
setupFilesAfterEnv: ['<rootDir>jest.setup.ts'],
|
setupFilesAfterEnv: ['<rootDir>jest.setup.ts'],
|
||||||
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
testPathIgnorePatterns: ['/node_modules/', '/public/'],
|
||||||
|
|||||||
@ -48,6 +48,8 @@
|
|||||||
"@signozhq/design-tokens": "1.1.4",
|
"@signozhq/design-tokens": "1.1.4",
|
||||||
"@signozhq/input": "0.0.2",
|
"@signozhq/input": "0.0.2",
|
||||||
"@signozhq/popover": "0.0.0",
|
"@signozhq/popover": "0.0.0",
|
||||||
|
"@signozhq/sonner": "0.1.0",
|
||||||
|
"@signozhq/table": "0.3.4",
|
||||||
"@tanstack/react-table": "8.20.6",
|
"@tanstack/react-table": "8.20.6",
|
||||||
"@tanstack/react-virtual": "3.11.2",
|
"@tanstack/react-virtual": "3.11.2",
|
||||||
"@uiw/codemirror-theme-copilot": "4.23.11",
|
"@uiw/codemirror-theme-copilot": "4.23.11",
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import {
|
|||||||
} from 'container/LogDetailedView/utils';
|
} from 'container/LogDetailedView/utils';
|
||||||
import useInitialQuery from 'container/LogsExplorerContext/useInitialQuery';
|
import useInitialQuery from 'container/LogsExplorerContext/useInitialQuery';
|
||||||
import { useOptionsMenu } from 'container/OptionsMenu';
|
import { useOptionsMenu } from 'container/OptionsMenu';
|
||||||
|
import { useCopyLogLink } from 'hooks/logs/useCopyLogLink';
|
||||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
@ -94,6 +95,8 @@ function LogDetailInner({
|
|||||||
|
|
||||||
const { notifications } = useNotifications();
|
const { notifications } = useNotifications();
|
||||||
|
|
||||||
|
const { onLogCopy } = useCopyLogLink(log?.id);
|
||||||
|
|
||||||
const LogJsonData = log ? aggregateAttributesResourcesToString(log) : '';
|
const LogJsonData = log ? aggregateAttributesResourcesToString(log) : '';
|
||||||
|
|
||||||
const handleModeChange = (e: RadioChangeEvent): void => {
|
const handleModeChange = (e: RadioChangeEvent): void => {
|
||||||
@ -333,6 +336,14 @@ function LogDetailInner({
|
|||||||
onClick={handleFilterVisible}
|
onClick={handleFilterVisible}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Tooltip title="Copy Log Link" placement="left" aria-label="Copy Log Link">
|
||||||
|
<Button
|
||||||
|
className="action-btn"
|
||||||
|
icon={<Copy size={16} />}
|
||||||
|
onClick={onLogCopy}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
{isFilterVisible && contextQuery?.builder.queryData[0] && (
|
{isFilterVisible && contextQuery?.builder.queryData[0] && (
|
||||||
<div className="log-detail-drawer-query-container">
|
<div className="log-detail-drawer-query-container">
|
||||||
|
|||||||
@ -56,6 +56,8 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
|||||||
.map(({ name }) => ({
|
.map(({ name }) => ({
|
||||||
title: name,
|
title: name,
|
||||||
dataIndex: name,
|
dataIndex: name,
|
||||||
|
accessorKey: name,
|
||||||
|
id: name.toLowerCase().replace(/\./g, '_'),
|
||||||
key: name,
|
key: name,
|
||||||
render: (field): ColumnTypeRender<Record<string, unknown>> => ({
|
render: (field): ColumnTypeRender<Record<string, unknown>> => ({
|
||||||
props: {
|
props: {
|
||||||
@ -83,7 +85,10 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
|||||||
// We do not need any title and data index for the log state indicator
|
// We do not need any title and data index for the log state indicator
|
||||||
title: '',
|
title: '',
|
||||||
dataIndex: '',
|
dataIndex: '',
|
||||||
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
key: 'state-indicator',
|
key: 'state-indicator',
|
||||||
|
accessorKey: 'state-indicator',
|
||||||
|
id: 'state-indicator',
|
||||||
render: (_, item): ColumnTypeRender<Record<string, unknown>> => ({
|
render: (_, item): ColumnTypeRender<Record<string, unknown>> => ({
|
||||||
children: (
|
children: (
|
||||||
<div className={cx('state-indicator', fontSize)}>
|
<div className={cx('state-indicator', fontSize)}>
|
||||||
@ -101,6 +106,8 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
|||||||
title: 'timestamp',
|
title: 'timestamp',
|
||||||
dataIndex: 'timestamp',
|
dataIndex: 'timestamp',
|
||||||
key: 'timestamp',
|
key: 'timestamp',
|
||||||
|
accessorKey: 'timestamp',
|
||||||
|
id: 'timestamp',
|
||||||
// https://github.com/ant-design/ant-design/discussions/36886
|
// https://github.com/ant-design/ant-design/discussions/36886
|
||||||
render: (
|
render: (
|
||||||
field: string | number,
|
field: string | number,
|
||||||
@ -135,6 +142,8 @@ export const useTableView = (props: UseTableViewProps): UseTableViewResult => {
|
|||||||
title: 'body',
|
title: 'body',
|
||||||
dataIndex: 'body',
|
dataIndex: 'body',
|
||||||
key: 'body',
|
key: 'body',
|
||||||
|
accessorKey: 'body',
|
||||||
|
id: 'body',
|
||||||
render: (
|
render: (
|
||||||
field: string | number,
|
field: string | number,
|
||||||
): ColumnTypeRender<Record<string, unknown>> => ({
|
): ColumnTypeRender<Record<string, unknown>> => ({
|
||||||
|
|||||||
@ -33,4 +33,5 @@ export enum LOCALSTORAGE {
|
|||||||
QUICK_FILTERS_SETTINGS_ANNOUNCEMENT = 'QUICK_FILTERS_SETTINGS_ANNOUNCEMENT',
|
QUICK_FILTERS_SETTINGS_ANNOUNCEMENT = 'QUICK_FILTERS_SETTINGS_ANNOUNCEMENT',
|
||||||
FUNNEL_STEPS = 'FUNNEL_STEPS',
|
FUNNEL_STEPS = 'FUNNEL_STEPS',
|
||||||
LAST_USED_CUSTOM_TIME_RANGES = 'LAST_USED_CUSTOM_TIME_RANGES',
|
LAST_USED_CUSTOM_TIME_RANGES = 'LAST_USED_CUSTOM_TIME_RANGES',
|
||||||
|
SHOW_FREQUENCY_CHART = 'SHOW_FREQUENCY_CHART',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import './AppLayout.styles.scss';
|
import './AppLayout.styles.scss';
|
||||||
|
|
||||||
import * as Sentry from '@sentry/react';
|
import * as Sentry from '@sentry/react';
|
||||||
|
import { Toaster } from '@signozhq/sonner';
|
||||||
import { Flex } from 'antd';
|
import { Flex } from 'antd';
|
||||||
import getLocalStorageApi from 'api/browser/localstorage/get';
|
import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||||
import setLocalStorageApi from 'api/browser/localstorage/set';
|
import setLocalStorageApi from 'api/browser/localstorage/set';
|
||||||
@ -852,6 +853,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
{showChangelogModal && changelog && (
|
{showChangelogModal && changelog && (
|
||||||
<ChangelogModal changelog={changelog} onClose={toggleChangelogModal} />
|
<ChangelogModal changelog={changelog} onClose={toggleChangelogModal} />
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<Toaster />
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
.explorer-options-container {
|
.explorer-options-container {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 8px;
|
bottom: 0px;
|
||||||
left: calc(50% + 240px);
|
left: calc(50% + 240px);
|
||||||
transform: translate(calc(-50% - 120px), 0);
|
transform: translate(calc(-50% - 120px), 0);
|
||||||
transition: left 0.2s linear;
|
transition: left 0.2s linear;
|
||||||
@ -74,6 +74,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
|
||||||
.ant-select-selector {
|
.ant-select-selector {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.explorer-show-btn {
|
.explorer-show-btn {
|
||||||
border-radius: 10px 10px 0px 0px;
|
border-radius: 6px 6px 0px 0px;
|
||||||
border: 1px solid var(--bg-slate-400);
|
border: 1px solid var(--bg-slate-400);
|
||||||
background: rgba(22, 24, 29, 0.4);
|
background: rgba(22, 24, 29, 0.4);
|
||||||
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.25);
|
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.25);
|
||||||
|
|||||||
@ -14,4 +14,7 @@ export const ContentWrapper = styled(Row)`
|
|||||||
|
|
||||||
export const Wrapper = styled.div`
|
export const Wrapper = styled.div`
|
||||||
padding-bottom: 4rem;
|
padding-bottom: 4rem;
|
||||||
|
padding-top: 1rem;
|
||||||
|
padding-left: 1rem;
|
||||||
|
padding-right: 1rem;
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -1,14 +0,0 @@
|
|||||||
import { Card } from 'antd';
|
|
||||||
import styled from 'styled-components';
|
|
||||||
|
|
||||||
export const CardStyled = styled(Card)`
|
|
||||||
border: none !important;
|
|
||||||
position: relative;
|
|
||||||
margin-bottom: 16px;
|
|
||||||
.ant-card-body {
|
|
||||||
height: 200px;
|
|
||||||
min-height: 200px;
|
|
||||||
padding: 0 16px 16px 16px;
|
|
||||||
font-family: 'Geist Mono';
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
.logs-frequency-chart-container {
|
||||||
|
height: 200px;
|
||||||
|
min-height: 200px;
|
||||||
|
border-bottom: 1px solid #262626;
|
||||||
|
|
||||||
|
.ant-card-body {
|
||||||
|
height: 200px;
|
||||||
|
min-height: 200px;
|
||||||
|
padding: 0 16px 16px 16px;
|
||||||
|
font-family: 'Geist Mono';
|
||||||
|
}
|
||||||
|
|
||||||
|
.logs-frequency-chart-loading {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightMode {
|
||||||
|
.logs-frequency-chart-container {
|
||||||
|
border-bottom: 1px solid var(--bg-vanilla-300);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
import './LogsExplorerChart.styles.scss';
|
||||||
|
|
||||||
import Graph from 'components/Graph';
|
import Graph from 'components/Graph';
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
@ -15,7 +17,6 @@ import { AppState } from 'store/reducers';
|
|||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import { LogsExplorerChartProps } from './LogsExplorerChart.interfaces';
|
import { LogsExplorerChartProps } from './LogsExplorerChart.interfaces';
|
||||||
import { CardStyled } from './LogsExplorerChart.styled';
|
|
||||||
import { getColorsForSeverityLabels } from './utils';
|
import { getColorsForSeverityLabels } from './utils';
|
||||||
|
|
||||||
function LogsExplorerChart({
|
function LogsExplorerChart({
|
||||||
@ -100,9 +101,11 @@ function LogsExplorerChart({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CardStyled className={className}>
|
<div className={`${className} logs-frequency-chart-container`}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
<div className="logs-frequency-chart-loading">
|
||||||
<Spinner size="default" height="100%" />
|
<Spinner size="default" height="100%" />
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Graph
|
<Graph
|
||||||
name="logsExplorerChart"
|
name="logsExplorerChart"
|
||||||
@ -115,7 +118,7 @@ function LogsExplorerChart({
|
|||||||
maxTime={chartMaxTime}
|
maxTime={chartMaxTime}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</CardStyled>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,240 @@
|
|||||||
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
|
import { ColumnDef, DataTable, Row } from '@signozhq/table';
|
||||||
|
import LogDetail from 'components/LogDetail';
|
||||||
|
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||||
|
import LogStateIndicator from 'components/Logs/LogStateIndicator/LogStateIndicator';
|
||||||
|
import { getLogIndicatorTypeForTable } from 'components/Logs/LogStateIndicator/utils';
|
||||||
|
import { useTableView } from 'components/Logs/TableView/useTableView';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import { QueryParams } from 'constants/query';
|
||||||
|
import { FontSize } from 'container/OptionsMenu/types';
|
||||||
|
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||||
|
import useDragColumns from 'hooks/useDragColumns';
|
||||||
|
import { getDraggedColumns } from 'hooks/useDragColumns/utils';
|
||||||
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
|
import { isEmpty, isEqual } from 'lodash-es';
|
||||||
|
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
||||||
|
import { ILog } from 'types/api/logs/log';
|
||||||
|
|
||||||
|
interface ColumnViewProps {
|
||||||
|
logs: ILog[];
|
||||||
|
onLoadMore: () => void;
|
||||||
|
selectedFields: any[];
|
||||||
|
isLoading: boolean;
|
||||||
|
isFetching: boolean;
|
||||||
|
|
||||||
|
isFrequencyChartVisible: boolean;
|
||||||
|
options: {
|
||||||
|
maxLinesPerRow: number;
|
||||||
|
fontSize: FontSize;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function ColumnView({
|
||||||
|
logs,
|
||||||
|
onLoadMore,
|
||||||
|
selectedFields,
|
||||||
|
isLoading,
|
||||||
|
isFetching,
|
||||||
|
isFrequencyChartVisible,
|
||||||
|
options,
|
||||||
|
}: ColumnViewProps): JSX.Element {
|
||||||
|
const {
|
||||||
|
activeLog,
|
||||||
|
onSetActiveLog: handleSetActiveLog,
|
||||||
|
onClearActiveLog: handleClearActiveLog,
|
||||||
|
onAddToQuery: handleAddToQuery,
|
||||||
|
onGroupByAttribute: handleGroupByAttribute,
|
||||||
|
} = useActiveLog();
|
||||||
|
|
||||||
|
const { queryData: activeLogId } = useUrlQueryData<string | null>(
|
||||||
|
QueryParams.activeLogId,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
const scrollToIndexRef = useRef<
|
||||||
|
| ((
|
||||||
|
rowIndex: number,
|
||||||
|
options?: { align?: 'start' | 'center' | 'end' },
|
||||||
|
) => void)
|
||||||
|
| undefined
|
||||||
|
>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeLogId) {
|
||||||
|
const log = logs.find(({ id }) => id === activeLogId);
|
||||||
|
|
||||||
|
if (log) {
|
||||||
|
handleSetActiveLog(log);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [activeLogId, logs, handleSetActiveLog]);
|
||||||
|
|
||||||
|
const tableViewProps = {
|
||||||
|
logs,
|
||||||
|
fields: selectedFields,
|
||||||
|
linesPerRow: options.maxLinesPerRow as number,
|
||||||
|
fontSize: options.fontSize as FontSize,
|
||||||
|
appendTo: 'end' as const,
|
||||||
|
activeLogIndex: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const { dataSource, columns } = useTableView({
|
||||||
|
...tableViewProps,
|
||||||
|
onClickExpand: handleSetActiveLog,
|
||||||
|
onOpenLogsContext: handleClearActiveLog,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { draggedColumns, onColumnOrderChange } = useDragColumns<
|
||||||
|
Record<string, unknown>
|
||||||
|
>(LOCALSTORAGE.LOGS_LIST_COLUMNS);
|
||||||
|
|
||||||
|
const tableColumns = useMemo(
|
||||||
|
() => getDraggedColumns<Record<string, unknown>>(columns, draggedColumns),
|
||||||
|
[columns, draggedColumns],
|
||||||
|
);
|
||||||
|
|
||||||
|
const scrollToLog = useCallback(
|
||||||
|
(logId: string): void => {
|
||||||
|
const logIndex = logs.findIndex((log) => log.id === logId);
|
||||||
|
|
||||||
|
if (logIndex !== -1 && scrollToIndexRef.current) {
|
||||||
|
scrollToIndexRef.current(logIndex, { align: 'center' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[logs],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (activeLogId) {
|
||||||
|
scrollToLog(activeLogId);
|
||||||
|
}
|
||||||
|
}, [activeLogId]);
|
||||||
|
|
||||||
|
const args = {
|
||||||
|
columns,
|
||||||
|
tableId: 'virtualized-infinite-reorder-resize',
|
||||||
|
enableSorting: false,
|
||||||
|
enableFiltering: false,
|
||||||
|
enableGlobalFilter: false,
|
||||||
|
enableColumnReordering: true,
|
||||||
|
enableColumnResizing: true,
|
||||||
|
enableColumnPinning: false,
|
||||||
|
enableRowSelection: false,
|
||||||
|
enablePagination: false,
|
||||||
|
showHeaders: true,
|
||||||
|
defaultColumnWidth: 180,
|
||||||
|
minColumnWidth: 80,
|
||||||
|
maxColumnWidth: 480,
|
||||||
|
// Virtualization + Infinite Scroll
|
||||||
|
enableVirtualization: true,
|
||||||
|
estimateRowSize: 56,
|
||||||
|
overscan: 50,
|
||||||
|
rowHeight: 56,
|
||||||
|
enableInfiniteScroll: true,
|
||||||
|
enableScrollRestoration: false,
|
||||||
|
fixedHeight: isFrequencyChartVisible ? 560 : 760,
|
||||||
|
enableDynamicRowHeight: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const selectedColumns = useMemo(
|
||||||
|
() =>
|
||||||
|
tableColumns.map((field) => ({
|
||||||
|
id: field.key?.toString().toLowerCase().replace(/\./g, '_'), // IMP - Replace dots with underscores as reordering does not work well for accessorKey with dots
|
||||||
|
// accessorKey: field.name,
|
||||||
|
accessorFn: (row: Record<string, string>): string =>
|
||||||
|
row[field.key as string] as string,
|
||||||
|
header: field.title as string,
|
||||||
|
// eslint-disable-next-line sonarjs/no-duplicate-string
|
||||||
|
size: field.key === 'state-indicator' ? 4 : 180,
|
||||||
|
minSize: field.key === 'state-indicator' ? 4 : 120,
|
||||||
|
maxSize: field.key === 'state-indicator' ? 4 : 1080,
|
||||||
|
pin: field.key === 'state-indicator' ? 'left' : 'none',
|
||||||
|
// eslint-disable-next-line react/no-unstable-nested-components
|
||||||
|
cell: ({
|
||||||
|
row,
|
||||||
|
getValue,
|
||||||
|
}: {
|
||||||
|
row: Row<Record<string, string>>;
|
||||||
|
getValue: () => string | JSX.Element;
|
||||||
|
}): string | JSX.Element => {
|
||||||
|
if (field.key === 'state-indicator') {
|
||||||
|
const type = getLogIndicatorTypeForTable(row.original);
|
||||||
|
const fontSize = options.fontSize as FontSize;
|
||||||
|
|
||||||
|
return <LogStateIndicator type={type} fontSize={fontSize} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`table-cell-content ${
|
||||||
|
row.original.id === activeLog?.id ? 'active-log' : ''
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{getValue()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
[tableColumns, options.fontSize, activeLog?.id],
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleColumnOrderChange = (newColumns: ColumnDef<any>[]): void => {
|
||||||
|
if (isEmpty(newColumns) || isEqual(newColumns, selectedColumns)) return;
|
||||||
|
|
||||||
|
const formattedColumns = newColumns.map((column) => ({
|
||||||
|
id: column.id,
|
||||||
|
header: column.header,
|
||||||
|
size: column.size,
|
||||||
|
minSize: column.minSize,
|
||||||
|
maxSize: column.maxSize,
|
||||||
|
key: column.id,
|
||||||
|
title: column.header as string,
|
||||||
|
dataIndex: column.id,
|
||||||
|
}));
|
||||||
|
|
||||||
|
onColumnOrderChange(formattedColumns);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRowClick = (row: Row<Record<string, unknown>>): void => {
|
||||||
|
const currentLog = logs.find(({ id }) => id === row.original.id);
|
||||||
|
|
||||||
|
handleSetActiveLog(currentLog as ILog);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`logs-list-table-view-container ${
|
||||||
|
options.fontSize as FontSize
|
||||||
|
} max-lines-${options.maxLinesPerRow as number}`}
|
||||||
|
data-max-lines-per-row={options.maxLinesPerRow}
|
||||||
|
data-font-size={options.fontSize}
|
||||||
|
>
|
||||||
|
<DataTable
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
{...args}
|
||||||
|
columns={selectedColumns as ColumnDef<Record<string, string>, unknown>[]}
|
||||||
|
data={dataSource}
|
||||||
|
hasMore
|
||||||
|
onLoadMore={onLoadMore}
|
||||||
|
loadingMore={isLoading || isFetching}
|
||||||
|
onColumnOrderChange={handleColumnOrderChange}
|
||||||
|
onRowClick={handleRowClick}
|
||||||
|
scrollToIndexRef={scrollToIndexRef}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{activeLog && (
|
||||||
|
<LogDetail
|
||||||
|
selectedTab={VIEW_TYPES.OVERVIEW}
|
||||||
|
log={activeLog}
|
||||||
|
onClose={handleClearActiveLog}
|
||||||
|
onAddToQuery={handleAddToQuery}
|
||||||
|
onClickActionItem={handleAddToQuery}
|
||||||
|
onGroupByAttribute={handleGroupByAttribute}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ColumnView;
|
||||||
@ -11,4 +11,5 @@ export type LogsExplorerListProps = {
|
|||||||
isError: boolean;
|
isError: boolean;
|
||||||
error?: Error | APIError;
|
error?: Error | APIError;
|
||||||
isFilterApplied: boolean;
|
isFilterApplied: boolean;
|
||||||
|
isFrequencyChartVisible: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -9,4 +9,362 @@
|
|||||||
letter-spacing: -0.005em;
|
letter-spacing: -0.005em;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
min-height: 500px;
|
min-height: 500px;
|
||||||
|
|
||||||
|
.logs-list-table-view-container {
|
||||||
|
.data-table-container {
|
||||||
|
border: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
th {
|
||||||
|
font-family: 'Space Mono', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
color: white !important;
|
||||||
|
|
||||||
|
.cursor-col-resize {
|
||||||
|
width: 2px !important;
|
||||||
|
cursor: col-resize !important;
|
||||||
|
opacity: 0.5 !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
font-family: 'Space Mono', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--bg-slate-400) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--bg-slate-400) !important;
|
||||||
|
background-color: var(--bg-ink-400) !important;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-ink-400) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
font-family: 'Space Mono', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 4px !important;
|
||||||
|
|
||||||
|
white-space: pre-wrap !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
text-overflow: ellipsis !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-slate-500) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--bg-slate-400) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:has(.active-log) {
|
||||||
|
background-color: rgba(
|
||||||
|
78,
|
||||||
|
116,
|
||||||
|
248,
|
||||||
|
0.5
|
||||||
|
) !important; // same as bg-robin-500
|
||||||
|
color: var(--text-vanilla-100) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thead {
|
||||||
|
z-index: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.log-state-indicator {
|
||||||
|
padding-left: 0 !important;
|
||||||
|
|
||||||
|
.line {
|
||||||
|
margin: 0 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticky-header-table-container {
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 0.4rem;
|
||||||
|
height: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--bg-slate-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--bg-slate-200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.medium {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
font-size: 14px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
font-size: 16px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.max-lines-1 {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='1'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='2'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='3'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 3;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='4'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 4;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='5'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 5;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='6'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 6;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='7'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 7;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='8'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 8;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='9'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 9;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&[data-max-lines-per-row='10'] {
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
td {
|
||||||
|
.table-cell-content {
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
-webkit-line-clamp: 10;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.lightMode {
|
||||||
|
.logs-list-view-container {
|
||||||
|
.logs-list-table-view-container {
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
tr {
|
||||||
|
th {
|
||||||
|
color: var(--text-ink-500) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--bg-vanilla-300) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--bg-vanilla-300) !important;
|
||||||
|
background-color: var(--bg-vanilla-100) !important;
|
||||||
|
|
||||||
|
tr {
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-vanilla-300) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tbody {
|
||||||
|
tr {
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--bg-vanilla-300) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
border-bottom: 1px solid var(--bg-vanilla-300) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sticky-header-table-container {
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--bg-vanilla-300);
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--bg-vanilla-100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import { ILog } from 'types/api/logs/log';
|
|||||||
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
import { DataSource, StringOperators } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
import NoLogs from '../NoLogs/NoLogs';
|
import NoLogs from '../NoLogs/NoLogs';
|
||||||
import InfinityTableView from './InfinityTableView';
|
import ColumnView from './ColumnView/ColumnView';
|
||||||
import { LogsExplorerListProps } from './LogsExplorerList.interfaces';
|
import { LogsExplorerListProps } from './LogsExplorerList.interfaces';
|
||||||
import { InfinityWrapperStyled } from './styles';
|
import { InfinityWrapperStyled } from './styles';
|
||||||
import {
|
import {
|
||||||
@ -48,6 +48,7 @@ function LogsExplorerList({
|
|||||||
isError,
|
isError,
|
||||||
error,
|
error,
|
||||||
isFilterApplied,
|
isFilterApplied,
|
||||||
|
isFrequencyChartVisible,
|
||||||
}: LogsExplorerListProps): JSX.Element {
|
}: LogsExplorerListProps): JSX.Element {
|
||||||
const ref = useRef<VirtuosoHandle>(null);
|
const ref = useRef<VirtuosoHandle>(null);
|
||||||
|
|
||||||
@ -90,6 +91,7 @@ function LogsExplorerList({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [isLoading, isFetching, isError, logs.length]);
|
}, [isLoading, isFetching, isError, logs.length]);
|
||||||
|
|
||||||
const getItemContent = useCallback(
|
const getItemContent = useCallback(
|
||||||
(_: number, log: ILog): JSX.Element => {
|
(_: number, log: ILog): JSX.Element => {
|
||||||
if (options.format === 'raw') {
|
if (options.format === 'raw') {
|
||||||
@ -128,75 +130,6 @@ function LogsExplorerList({
|
|||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderContent = useMemo(() => {
|
|
||||||
const components = isLoading
|
|
||||||
? {
|
|
||||||
Footer,
|
|
||||||
}
|
|
||||||
: {};
|
|
||||||
|
|
||||||
if (options.format === 'table') {
|
|
||||||
return (
|
|
||||||
<InfinityTableView
|
|
||||||
ref={ref}
|
|
||||||
isLoading={isLoading}
|
|
||||||
tableViewProps={{
|
|
||||||
logs,
|
|
||||||
fields: selectedFields,
|
|
||||||
linesPerRow: options.maxLines,
|
|
||||||
fontSize: options.fontSize,
|
|
||||||
appendTo: 'end',
|
|
||||||
activeLogIndex,
|
|
||||||
}}
|
|
||||||
infitiyTableProps={{ onEndReached }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMarginTop(): string {
|
|
||||||
switch (options.fontSize) {
|
|
||||||
case FontSize.SMALL:
|
|
||||||
return '10px';
|
|
||||||
case FontSize.MEDIUM:
|
|
||||||
return '12px';
|
|
||||||
case FontSize.LARGE:
|
|
||||||
return '15px';
|
|
||||||
default:
|
|
||||||
return '15px';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Card
|
|
||||||
style={{ width: '100%', marginTop: getMarginTop() }}
|
|
||||||
bodyStyle={CARD_BODY_STYLE}
|
|
||||||
>
|
|
||||||
<OverlayScrollbar isVirtuoso>
|
|
||||||
<Virtuoso
|
|
||||||
key={activeLogIndex || 'logs-virtuoso'}
|
|
||||||
ref={ref}
|
|
||||||
initialTopMostItemIndex={activeLogIndex !== -1 ? activeLogIndex : 0}
|
|
||||||
data={logs}
|
|
||||||
endReached={onEndReached}
|
|
||||||
totalCount={logs.length}
|
|
||||||
itemContent={getItemContent}
|
|
||||||
components={components}
|
|
||||||
/>
|
|
||||||
</OverlayScrollbar>
|
|
||||||
</Card>
|
|
||||||
);
|
|
||||||
}, [
|
|
||||||
isLoading,
|
|
||||||
options.format,
|
|
||||||
options.maxLines,
|
|
||||||
options.fontSize,
|
|
||||||
activeLogIndex,
|
|
||||||
logs,
|
|
||||||
onEndReached,
|
|
||||||
getItemContent,
|
|
||||||
selectedFields,
|
|
||||||
]);
|
|
||||||
|
|
||||||
const isTraceToLogsNavigation = useMemo(() => {
|
const isTraceToLogsNavigation = useMemo(() => {
|
||||||
if (!currentStagedQueryData) return false;
|
if (!currentStagedQueryData) return false;
|
||||||
return isTraceToLogsQuery(currentStagedQueryData);
|
return isTraceToLogsQuery(currentStagedQueryData);
|
||||||
@ -236,6 +169,83 @@ function LogsExplorerList({
|
|||||||
return getEmptyLogsListConfig(handleClearFilters);
|
return getEmptyLogsListConfig(handleClearFilters);
|
||||||
}, [isTraceToLogsNavigation, handleClearFilters]);
|
}, [isTraceToLogsNavigation, handleClearFilters]);
|
||||||
|
|
||||||
|
const handleLoadMore = useCallback(() => {
|
||||||
|
if (isLoading || isFetching) return;
|
||||||
|
|
||||||
|
onEndReached(logs.length);
|
||||||
|
}, [isLoading, isFetching, onEndReached, logs.length]);
|
||||||
|
|
||||||
|
const renderContent = useMemo(() => {
|
||||||
|
const components = isLoading
|
||||||
|
? {
|
||||||
|
Footer,
|
||||||
|
}
|
||||||
|
: {};
|
||||||
|
|
||||||
|
if (options.format === 'table') {
|
||||||
|
return (
|
||||||
|
<ColumnView
|
||||||
|
logs={logs}
|
||||||
|
onLoadMore={handleLoadMore}
|
||||||
|
selectedFields={selectedFields}
|
||||||
|
isLoading={isLoading}
|
||||||
|
isFetching={isFetching}
|
||||||
|
options={{
|
||||||
|
maxLinesPerRow: options.maxLines,
|
||||||
|
fontSize: options.fontSize,
|
||||||
|
}}
|
||||||
|
isFrequencyChartVisible={isFrequencyChartVisible}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMarginTop(): string {
|
||||||
|
switch (options.fontSize) {
|
||||||
|
case FontSize.SMALL:
|
||||||
|
return '10px';
|
||||||
|
case FontSize.MEDIUM:
|
||||||
|
return '12px';
|
||||||
|
case FontSize.LARGE:
|
||||||
|
return '15px';
|
||||||
|
default:
|
||||||
|
return '15px';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InfinityWrapperStyled data-testid="logs-list-virtuoso">
|
||||||
|
<Card
|
||||||
|
style={{ width: '100%', marginTop: getMarginTop() }}
|
||||||
|
bodyStyle={CARD_BODY_STYLE}
|
||||||
|
>
|
||||||
|
<OverlayScrollbar isVirtuoso>
|
||||||
|
<Virtuoso
|
||||||
|
key={activeLogIndex || 'logs-virtuoso'}
|
||||||
|
ref={ref}
|
||||||
|
initialTopMostItemIndex={activeLogIndex !== -1 ? activeLogIndex : 0}
|
||||||
|
data={logs}
|
||||||
|
endReached={onEndReached}
|
||||||
|
totalCount={logs.length}
|
||||||
|
itemContent={getItemContent}
|
||||||
|
components={components}
|
||||||
|
/>
|
||||||
|
</OverlayScrollbar>
|
||||||
|
</Card>
|
||||||
|
</InfinityWrapperStyled>
|
||||||
|
);
|
||||||
|
}, [
|
||||||
|
isLoading,
|
||||||
|
activeLogIndex,
|
||||||
|
handleLoadMore,
|
||||||
|
isFetching,
|
||||||
|
logs,
|
||||||
|
onEndReached,
|
||||||
|
getItemContent,
|
||||||
|
selectedFields,
|
||||||
|
isFrequencyChartVisible,
|
||||||
|
options,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs-list-view-container">
|
<div className="logs-list-view-container">
|
||||||
{(isLoading || (isFetching && logs.length === 0)) && <LogsLoading />}
|
{(isLoading || (isFetching && logs.length === 0)) && <LogsLoading />}
|
||||||
@ -264,9 +274,7 @@ function LogsExplorerList({
|
|||||||
|
|
||||||
{!isLoading && !isError && logs.length > 0 && (
|
{!isLoading && !isError && logs.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<InfinityWrapperStyled data-testid="logs-list-virtuoso">
|
|
||||||
{renderContent}
|
{renderContent}
|
||||||
</InfinityWrapperStyled>
|
|
||||||
|
|
||||||
<LogDetail
|
<LogDetail
|
||||||
selectedTab={VIEW_TYPES.OVERVIEW}
|
selectedTab={VIEW_TYPES.OVERVIEW}
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
padding-bottom: 60px;
|
padding-bottom: 10px;
|
||||||
|
|
||||||
.views-tabs-container {
|
.views-tabs-container {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
@ -216,7 +216,10 @@
|
|||||||
background-color: var(--bg-ink-500);
|
background-color: var(--bg-ink-500);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logs-histogram {
|
.logs-frequency-chart-container {
|
||||||
|
padding: 0px 8px;
|
||||||
|
|
||||||
|
.logs-frequency-chart {
|
||||||
.ant-card-body {
|
.ant-card-body {
|
||||||
height: 140px;
|
height: 140px;
|
||||||
min-height: 140px;
|
min-height: 140px;
|
||||||
@ -227,6 +230,7 @@
|
|||||||
margin-bottom: 0px;
|
margin-bottom: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.lightMode {
|
.lightMode {
|
||||||
.logs-explorer-views-container {
|
.logs-explorer-views-container {
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
import './LogsExplorerViews.styles.scss';
|
import './LogsExplorerViews.styles.scss';
|
||||||
|
|
||||||
import { Button, Switch, Typography } from 'antd';
|
import { Button, Switch, Typography } from 'antd';
|
||||||
|
import getFromLocalstorage from 'api/browser/localstorage/get';
|
||||||
|
import setToLocalstorage from 'api/browser/localstorage/set';
|
||||||
import { getQueryStats, WsDataEvent } from 'api/common/getQueryStats';
|
import { getQueryStats, WsDataEvent } from 'api/common/getQueryStats';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
import { getYAxisFormattedValue } from 'components/Graph/yAxisConfig';
|
||||||
@ -103,7 +105,13 @@ function LogsExplorerViewsContainer({
|
|||||||
}): JSX.Element {
|
}): JSX.Element {
|
||||||
const { safeNavigate } = useSafeNavigate();
|
const { safeNavigate } = useSafeNavigate();
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
const [showFrequencyChart, setShowFrequencyChart] = useState(true);
|
|
||||||
|
const [showFrequencyChart, setShowFrequencyChart] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const frequencyChart = getFromLocalstorage(LOCALSTORAGE.SHOW_FREQUENCY_CHART);
|
||||||
|
setShowFrequencyChart(frequencyChart === 'true');
|
||||||
|
}, []);
|
||||||
|
|
||||||
// this is to respect the panel type present in the URL rather than defaulting it to list always.
|
// this is to respect the panel type present in the URL rather than defaulting it to list always.
|
||||||
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
|
const panelTypes = useGetPanelTypesQueryParam(PANEL_TYPES.LIST);
|
||||||
@ -672,6 +680,18 @@ function LogsExplorerViewsContainer({
|
|||||||
[logs, timezone.value],
|
[logs, timezone.value],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleToggleFrequencyChart = useCallback(() => {
|
||||||
|
const newShowFrequencyChart = !showFrequencyChart;
|
||||||
|
|
||||||
|
// store the value in local storage
|
||||||
|
setToLocalstorage(
|
||||||
|
LOCALSTORAGE.SHOW_FREQUENCY_CHART,
|
||||||
|
newShowFrequencyChart?.toString() || 'false',
|
||||||
|
);
|
||||||
|
|
||||||
|
setShowFrequencyChart(newShowFrequencyChart);
|
||||||
|
}, [showFrequencyChart]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="logs-explorer-views-container">
|
<div className="logs-explorer-views-container">
|
||||||
<div className="logs-explorer-views-types">
|
<div className="logs-explorer-views-types">
|
||||||
@ -685,7 +705,7 @@ function LogsExplorerViewsContainer({
|
|||||||
size="small"
|
size="small"
|
||||||
checked={showFrequencyChart}
|
checked={showFrequencyChart}
|
||||||
defaultChecked
|
defaultChecked
|
||||||
onChange={(): void => setShowFrequencyChart(!showFrequencyChart)}
|
onChange={handleToggleFrequencyChart}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -761,12 +781,14 @@ function LogsExplorerViewsContainer({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{selectedPanelType === PANEL_TYPES.LIST && showFrequencyChart && (
|
{selectedPanelType === PANEL_TYPES.LIST && showFrequencyChart && (
|
||||||
|
<div className="logs-frequency-chart-container">
|
||||||
<LogsExplorerChart
|
<LogsExplorerChart
|
||||||
className="logs-histogram"
|
className="logs-frequency-chart"
|
||||||
isLoading={isFetchingListChartData || isLoadingListChartData}
|
isLoading={isFetchingListChartData || isLoadingListChartData}
|
||||||
data={chartData}
|
data={chartData}
|
||||||
isLogsExplorerViews={panelType === PANEL_TYPES.LIST}
|
isLogsExplorerViews={panelType === PANEL_TYPES.LIST}
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="logs-explorer-views-type-content">
|
<div className="logs-explorer-views-type-content">
|
||||||
@ -777,12 +799,12 @@ function LogsExplorerViewsContainer({
|
|||||||
currentStagedQueryData={listQuery}
|
currentStagedQueryData={listQuery}
|
||||||
logs={logs}
|
logs={logs}
|
||||||
onEndReached={handleEndReached}
|
onEndReached={handleEndReached}
|
||||||
|
isFrequencyChartVisible={showFrequencyChart}
|
||||||
isError={isError}
|
isError={isError}
|
||||||
error={error as APIError}
|
error={error as APIError}
|
||||||
isFilterApplied={!isEmpty(listQuery?.filters?.items)}
|
isFilterApplied={!isEmpty(listQuery?.filters?.items)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{selectedPanelType === PANEL_TYPES.TIME_SERIES && (
|
{selectedPanelType === PANEL_TYPES.TIME_SERIES && (
|
||||||
<TimeSeriesView
|
<TimeSeriesView
|
||||||
isLoading={isLoading || isFetching}
|
isLoading={isLoading || isFetching}
|
||||||
|
|||||||
@ -65,17 +65,6 @@ const useOptionsMenu = ({
|
|||||||
const [isFocused, setIsFocused] = useState<boolean>(false);
|
const [isFocused, setIsFocused] = useState<boolean>(false);
|
||||||
const debouncedSearchText = useDebounce(searchText, 300);
|
const debouncedSearchText = useDebounce(searchText, 300);
|
||||||
|
|
||||||
// const initialQueryParams = useMemo(
|
|
||||||
// () => ({
|
|
||||||
// searchText: '',
|
|
||||||
// aggregateAttribute: '',
|
|
||||||
// tagType: undefined,
|
|
||||||
// dataSource,
|
|
||||||
// aggregateOperator,
|
|
||||||
// }),
|
|
||||||
// [dataSource, aggregateOperator],
|
|
||||||
// );
|
|
||||||
|
|
||||||
const initialQueryParamsV5: QueryKeyRequestProps = useMemo(
|
const initialQueryParamsV5: QueryKeyRequestProps = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
signal: dataSource,
|
signal: dataSource,
|
||||||
@ -89,22 +78,6 @@ const useOptionsMenu = ({
|
|||||||
redirectWithQuery: redirectWithOptionsData,
|
redirectWithQuery: redirectWithOptionsData,
|
||||||
} = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery);
|
} = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery);
|
||||||
|
|
||||||
// const initialQueries = useMemo(
|
|
||||||
// () =>
|
|
||||||
// initialOptions?.selectColumns?.map((column) => ({
|
|
||||||
// queryKey: column,
|
|
||||||
// queryFn: (): Promise<
|
|
||||||
// SuccessResponse<IQueryAutocompleteResponse> | ErrorResponse
|
|
||||||
// > =>
|
|
||||||
// getAggregateKeys({
|
|
||||||
// ...initialQueryParams,
|
|
||||||
// searchText: column,
|
|
||||||
// }),
|
|
||||||
// enabled: !!column && !optionsQuery,
|
|
||||||
// })) || [],
|
|
||||||
// [initialOptions?.selectColumns, initialQueryParams, optionsQuery],
|
|
||||||
// );
|
|
||||||
|
|
||||||
const initialQueriesV5 = useMemo(
|
const initialQueriesV5 = useMemo(
|
||||||
() =>
|
() =>
|
||||||
initialOptions?.selectColumns?.map((column) => ({
|
initialOptions?.selectColumns?.map((column) => ({
|
||||||
|
|||||||
@ -42,11 +42,11 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
&.active-tab {
|
&.active-tab {
|
||||||
background-color: var(--bg-ink-500);
|
background-color: var(--bg-robin-500);
|
||||||
border-bottom: 1px solid var(--bg-ink-500);
|
border-bottom: 1px solid var(--bg-robin-500);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background-color: var(--bg-ink-500) !important;
|
background-color: var(--bg-robin-500) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,6 +12,7 @@ export type UseCopyLogLink = {
|
|||||||
isLogsExplorerPage: boolean;
|
isLogsExplorerPage: boolean;
|
||||||
activeLogId: string | null;
|
activeLogId: string | null;
|
||||||
onLogCopy: MouseEventHandler<HTMLElement>;
|
onLogCopy: MouseEventHandler<HTMLElement>;
|
||||||
|
onClearActiveLog: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type UseActiveLog = {
|
export type UseActiveLog = {
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
|
import { toast } from '@signozhq/sonner';
|
||||||
import { QueryParams } from 'constants/query';
|
import { QueryParams } from 'constants/query';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||||
import useUrlQuery from 'hooks/useUrlQuery';
|
import useUrlQuery from 'hooks/useUrlQuery';
|
||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
import {
|
import {
|
||||||
@ -21,9 +22,10 @@ import { UseCopyLogLink } from './types';
|
|||||||
|
|
||||||
export const useCopyLogLink = (logId?: string): UseCopyLogLink => {
|
export const useCopyLogLink = (logId?: string): UseCopyLogLink => {
|
||||||
const urlQuery = useUrlQuery();
|
const urlQuery = useUrlQuery();
|
||||||
const { pathname } = useLocation();
|
const { pathname, search } = useLocation();
|
||||||
const [, setCopy] = useCopyToClipboard();
|
const [, setCopy] = useCopyToClipboard();
|
||||||
const { notifications } = useNotifications();
|
|
||||||
|
const { safeNavigate } = useSafeNavigate();
|
||||||
|
|
||||||
const { queryData: activeLogId } = useUrlQueryData<string | null>(
|
const { queryData: activeLogId } = useUrlQueryData<string | null>(
|
||||||
QueryParams.activeLogId,
|
QueryParams.activeLogId,
|
||||||
@ -58,13 +60,19 @@ export const useCopyLogLink = (logId?: string): UseCopyLogLink => {
|
|||||||
const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`;
|
const link = `${window.location.origin}${pathname}?${urlQuery.toString()}`;
|
||||||
|
|
||||||
setCopy(link);
|
setCopy(link);
|
||||||
notifications.success({
|
|
||||||
message: 'Copied to clipboard',
|
toast.success('Copied to clipboard', { position: 'top-right' });
|
||||||
});
|
|
||||||
},
|
},
|
||||||
[logId, urlQuery, minTime, maxTime, pathname, setCopy, notifications],
|
[logId, urlQuery, minTime, maxTime, pathname, setCopy],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onClearActiveLog = useCallback(() => {
|
||||||
|
const currentUrlQuery = new URLSearchParams(search);
|
||||||
|
currentUrlQuery.delete(QueryParams.activeLogId);
|
||||||
|
const newUrl = `${pathname}?${currentUrlQuery.toString()}`;
|
||||||
|
safeNavigate(newUrl);
|
||||||
|
}, [pathname, search, safeNavigate]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isActiveLog) return;
|
if (!isActiveLog) return;
|
||||||
|
|
||||||
@ -81,5 +89,6 @@ export const useCopyLogLink = (logId?: string): UseCopyLogLink => {
|
|||||||
isLogsExplorerPage,
|
isLogsExplorerPage,
|
||||||
activeLogId,
|
activeLogId,
|
||||||
onLogCopy,
|
onLogCopy,
|
||||||
|
onClearActiveLog,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -40,6 +40,13 @@ const useDragColumns = <T>(storageKey: LOCALSTORAGE): UseDragColumns<T> => {
|
|||||||
[handleRedirectWithDraggedColumns],
|
[handleRedirectWithDraggedColumns],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const onColumnOrderChange = useCallback(
|
||||||
|
(newColumns: ColumnsType<T>): void => {
|
||||||
|
handleRedirectWithDraggedColumns(newColumns);
|
||||||
|
},
|
||||||
|
[handleRedirectWithDraggedColumns],
|
||||||
|
);
|
||||||
|
|
||||||
const redirectWithNewDraggedColumns = useCallback(
|
const redirectWithNewDraggedColumns = useCallback(
|
||||||
async (localStorageColumns: string) => {
|
async (localStorageColumns: string) => {
|
||||||
let nextDraggedColumns: ColumnsType<T> = [];
|
let nextDraggedColumns: ColumnsType<T> = [];
|
||||||
@ -69,6 +76,7 @@ const useDragColumns = <T>(storageKey: LOCALSTORAGE): UseDragColumns<T> => {
|
|||||||
return {
|
return {
|
||||||
draggedColumns,
|
draggedColumns,
|
||||||
onDragColumns,
|
onDragColumns,
|
||||||
|
onColumnOrderChange,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,4 +7,5 @@ export type UseDragColumns<T> = {
|
|||||||
fromIndex: number,
|
fromIndex: number,
|
||||||
toIndex: number,
|
toIndex: number,
|
||||||
) => void;
|
) => void;
|
||||||
|
onColumnOrderChange: (newColumns: ColumnsType<T>) => void;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -105,7 +105,6 @@ const logsQueryServerRequest = (): void =>
|
|||||||
describe('Logs Explorer Tests', () => {
|
describe('Logs Explorer Tests', () => {
|
||||||
test('Logs Explorer default view test without data', async () => {
|
test('Logs Explorer default view test without data', async () => {
|
||||||
const {
|
const {
|
||||||
getByText,
|
|
||||||
getByRole,
|
getByRole,
|
||||||
queryByText,
|
queryByText,
|
||||||
getByTestId,
|
getByTestId,
|
||||||
@ -123,13 +122,10 @@ describe('Logs Explorer Tests', () => {
|
|||||||
</MemoryRouter>,
|
</MemoryRouter>,
|
||||||
);
|
);
|
||||||
|
|
||||||
// check the presence of frequency chart content
|
// by default is hidden, toggle the chart and check it's visibility
|
||||||
expect(getByText(frequencyChartContent)).toBeInTheDocument();
|
|
||||||
|
|
||||||
// toggle the chart and check it gets removed from the DOM
|
|
||||||
const histogramToggle = getByRole('switch');
|
const histogramToggle = getByRole('switch');
|
||||||
fireEvent.click(histogramToggle);
|
fireEvent.click(histogramToggle);
|
||||||
expect(queryByText(frequencyChartContent)).not.toBeInTheDocument();
|
expect(queryByText(frequencyChartContent)).toBeInTheDocument();
|
||||||
|
|
||||||
// check the presence of search bar and query builder and absence of clickhouse
|
// check the presence of search bar and query builder and absence of clickhouse
|
||||||
const searchView = getByTestId('search-view');
|
const searchView = getByTestId('search-view');
|
||||||
@ -277,10 +273,10 @@ describe('Logs Explorer Tests', () => {
|
|||||||
const histogramToggle = getByRole('switch');
|
const histogramToggle = getByRole('switch');
|
||||||
expect(histogramToggle).toBeInTheDocument();
|
expect(histogramToggle).toBeInTheDocument();
|
||||||
expect(histogramToggle).toBeChecked();
|
expect(histogramToggle).toBeChecked();
|
||||||
expect(queryByText(frequencyChartContent)).toBeInTheDocument();
|
|
||||||
|
|
||||||
// toggle the chart and check it gets removed from the DOM
|
// toggle the chart and check it gets removed from the DOM
|
||||||
await fireEvent.click(histogramToggle);
|
await fireEvent.click(histogramToggle);
|
||||||
|
expect(histogramToggle).not.toBeChecked();
|
||||||
expect(queryByText(frequencyChartContent)).not.toBeInTheDocument();
|
expect(queryByText(frequencyChartContent)).not.toBeInTheDocument();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -4284,6 +4284,38 @@
|
|||||||
tailwind-merge "^2.5.2"
|
tailwind-merge "^2.5.2"
|
||||||
tailwindcss-animate "^1.0.7"
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
|
"@signozhq/sonner@0.1.0":
|
||||||
|
version "0.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@signozhq/sonner/-/sonner-0.1.0.tgz#1310cc530c60459608246550eb977a1ae27b6ce4"
|
||||||
|
integrity sha512-P4gc1WdNiX89FZIAhIvR4Bj3sdL7VIpoM80L6otnoeLCZhyFfEOXHGAElvO2z7BuBqJBp1f3pdVDrBQNE6bhyw==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-icons" "^1.3.0"
|
||||||
|
"@radix-ui/react-slot" "^1.1.0"
|
||||||
|
class-variance-authority "^0.7.0"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
lucide-react "^0.445.0"
|
||||||
|
next-themes "^0.4.6"
|
||||||
|
sonner "^2.0.7"
|
||||||
|
tailwind-merge "^2.5.2"
|
||||||
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
|
"@signozhq/table@0.3.4":
|
||||||
|
version "0.3.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@signozhq/table/-/table-0.3.4.tgz#5f243f2977f21fe351207d4ae6db390e400a7933"
|
||||||
|
integrity sha512-5B3kxYrfNEE9TH1orxAl6CNlESnyNVOgEW08ji9u2kz+khUd0euMCnZPrTBbotRW/INoEwRQxyBPj3auaa3jjQ==
|
||||||
|
dependencies:
|
||||||
|
"@radix-ui/react-icons" "^1.3.0"
|
||||||
|
"@radix-ui/react-slot" "^1.1.0"
|
||||||
|
"@tanstack/react-table" "^8.21.3"
|
||||||
|
"@tanstack/react-virtual" "^3.13.9"
|
||||||
|
"@types/lodash-es" "^4.17.12"
|
||||||
|
class-variance-authority "^0.7.0"
|
||||||
|
clsx "^2.1.1"
|
||||||
|
lodash-es "^4.17.21"
|
||||||
|
lucide-react "^0.445.0"
|
||||||
|
tailwind-merge "^2.5.2"
|
||||||
|
tailwindcss-animate "^1.0.7"
|
||||||
|
|
||||||
"@sinclair/typebox@^0.25.16":
|
"@sinclair/typebox@^0.25.16":
|
||||||
version "0.25.24"
|
version "0.25.24"
|
||||||
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz"
|
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz"
|
||||||
@ -4327,6 +4359,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/table-core" "8.20.5"
|
"@tanstack/table-core" "8.20.5"
|
||||||
|
|
||||||
|
"@tanstack/react-table@^8.21.3":
|
||||||
|
version "8.21.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.3.tgz#2c38c747a5731c1a07174fda764b9c2b1fb5e91b"
|
||||||
|
integrity sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/table-core" "8.21.3"
|
||||||
|
|
||||||
"@tanstack/react-virtual@3.11.2":
|
"@tanstack/react-virtual@3.11.2":
|
||||||
version "3.11.2"
|
version "3.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz#d6b9bd999c181f0a2edce270c87a2febead04322"
|
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz#d6b9bd999c181f0a2edce270c87a2febead04322"
|
||||||
@ -4334,16 +4373,33 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@tanstack/virtual-core" "3.11.2"
|
"@tanstack/virtual-core" "3.11.2"
|
||||||
|
|
||||||
|
"@tanstack/react-virtual@^3.13.9":
|
||||||
|
version "3.13.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tanstack/react-virtual/-/react-virtual-3.13.12.tgz#d372dc2783739cc04ec1a728ca8203937687a819"
|
||||||
|
integrity sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==
|
||||||
|
dependencies:
|
||||||
|
"@tanstack/virtual-core" "3.13.12"
|
||||||
|
|
||||||
"@tanstack/table-core@8.20.5":
|
"@tanstack/table-core@8.20.5":
|
||||||
version "8.20.5"
|
version "8.20.5"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d"
|
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d"
|
||||||
integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==
|
integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg==
|
||||||
|
|
||||||
|
"@tanstack/table-core@8.21.3":
|
||||||
|
version "8.21.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.3.tgz#2977727d8fc8dfa079112d9f4d4c019110f1732c"
|
||||||
|
integrity sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==
|
||||||
|
|
||||||
"@tanstack/virtual-core@3.11.2":
|
"@tanstack/virtual-core@3.11.2":
|
||||||
version "3.11.2"
|
version "3.11.2"
|
||||||
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz#00409e743ac4eea9afe5b7708594d5fcebb00212"
|
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz#00409e743ac4eea9afe5b7708594d5fcebb00212"
|
||||||
integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==
|
integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==
|
||||||
|
|
||||||
|
"@tanstack/virtual-core@3.13.12":
|
||||||
|
version "3.13.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.13.12.tgz#1dff176df9cc8f93c78c5e46bcea11079b397578"
|
||||||
|
integrity sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==
|
||||||
|
|
||||||
"@testing-library/dom@^8.5.0":
|
"@testing-library/dom@^8.5.0":
|
||||||
version "8.20.0"
|
version "8.20.0"
|
||||||
resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz"
|
resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-8.20.0.tgz"
|
||||||
@ -4825,6 +4881,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/lodash-es@^4.17.12":
|
||||||
|
version "4.17.12"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/lodash-es/-/lodash-es-4.17.12.tgz#65f6d1e5f80539aa7cfbfc962de5def0cf4f341b"
|
||||||
|
integrity sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/lodash" "*"
|
||||||
|
|
||||||
"@types/lodash-es@^4.17.4":
|
"@types/lodash-es@^4.17.4":
|
||||||
version "4.17.7"
|
version "4.17.7"
|
||||||
resolved "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.7.tgz"
|
resolved "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.7.tgz"
|
||||||
@ -13326,6 +13389,11 @@ new-array@^1.0.0:
|
|||||||
resolved "https://registry.npmjs.org/new-array/-/new-array-1.0.0.tgz"
|
resolved "https://registry.npmjs.org/new-array/-/new-array-1.0.0.tgz"
|
||||||
integrity sha512-K5AyFYbuHZ4e/ti52y7k18q8UHsS78FlRd85w2Fmsd6AkuLipDihPflKC0p3PN5i8II7+uHxo+CtkLiJDfmS5A==
|
integrity sha512-K5AyFYbuHZ4e/ti52y7k18q8UHsS78FlRd85w2Fmsd6AkuLipDihPflKC0p3PN5i8II7+uHxo+CtkLiJDfmS5A==
|
||||||
|
|
||||||
|
next-themes@^0.4.6:
|
||||||
|
version "0.4.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/next-themes/-/next-themes-0.4.6.tgz#8d7e92d03b8fea6582892a50a928c9b23502e8b6"
|
||||||
|
integrity sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==
|
||||||
|
|
||||||
ngraph.events@^1.0.0, ngraph.events@^1.2.1:
|
ngraph.events@^1.0.0, ngraph.events@^1.2.1:
|
||||||
version "1.2.2"
|
version "1.2.2"
|
||||||
resolved "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.2.2.tgz"
|
resolved "https://registry.npmjs.org/ngraph.events/-/ngraph.events-1.2.2.tgz"
|
||||||
@ -16430,6 +16498,11 @@ sockjs@^0.3.24:
|
|||||||
uuid "^8.3.2"
|
uuid "^8.3.2"
|
||||||
websocket-driver "^0.7.4"
|
websocket-driver "^0.7.4"
|
||||||
|
|
||||||
|
sonner@^2.0.7:
|
||||||
|
version "2.0.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/sonner/-/sonner-2.0.7.tgz#810c1487a67ec3370126e0f400dfb9edddc3e4f6"
|
||||||
|
integrity sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==
|
||||||
|
|
||||||
sort-asc@^0.1.0:
|
sort-asc@^0.1.0:
|
||||||
version "0.1.0"
|
version "0.1.0"
|
||||||
resolved "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz"
|
resolved "https://registry.npmjs.org/sort-asc/-/sort-asc-0.1.0.tgz"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user