makeCellCustomStyles(id)}
+ mantineTableHeadRowProps={{ style: { border: 'none' } }}
+ mantineTableHeadCellProps={{
+ style: {
+ fontWeight: 600,
+ fontSize: '0.65rem',
+ border: 'none',
+ padding: '0.5rem 1rem',
},
+ }}
+ mantineTableBodyRowProps={({ row }) => {
+ return {
+ onClick: (event) => {
+ event.preventDefault();
+ handleRowClick(row.index, event);
+ },
+ onContextMenu: (event) => {
+ event.preventDefault();
+ setContextMenu({
+ visible: true,
+ x: event.pageX,
+ y: event.pageY,
+ row: row.original,
+ });
+ },
+ style: {
+ border: rowNumber.includes(row.index) ? '2px solid #007BFF' : 'none',
+ background: row.index % 2 === 0 ? '#f8f9fa' : 'white',
+ transition: 'border 0.2s',
+ },
+ };
+ }}
+ mantineTableHeadProps={{
style: {
border: 'none',
- background: row.index % 2 === 0 ? '#f8f9fa' : 'white',
},
- };
- }}
- mantineTableHeadProps={{
- style: {
- border: 'none',
- },
- }}
- columns={columns}
- data={pageData}
- mantinePaperProps={{ style: { border: 'none' } }}
- enablePagination={false}
- enableColumnPinning={true}
- initialState={{
- columnPinning: {
- left: pinnedColumns,
- },
- }}
- enableStickyHeader={true}
- defaultColumn={{ minSize: 100 }}
- layoutMode="grid"
- state={{
- columnPinning: {
- left: pinnedColumns,
- },
- columnVisibility,
- columnOrder: orderedHeaders,
- }}
- mantineTableContainerProps={{
- style: {
- height: `calc(100vh - ${props.primaryHeaderHeight + LOGS_FOOTER_HEIGHT}px )`,
- },
- }}
- renderColumnActionsMenuItems={({ column }) => {
- return ;
- }}
- />
+ }}
+ columns={columns}
+ data={pageData}
+ mantinePaperProps={{ style: { border: 'none' } }}
+ enablePagination={false}
+ enableColumnPinning={true}
+ initialState={{
+ columnPinning: {
+ left: ['rowNumber'],
+ },
+ }}
+ enableStickyHeader={true}
+ defaultColumn={{ minSize: 100 }}
+ layoutMode="grid"
+ state={{
+ columnPinning: {
+ left: ['rowNumber'],
+ },
+ columnVisibility,
+ columnOrder: orderedHeaders,
+ }}
+ mantineTableContainerProps={{
+ style: {
+ height: `calc(100vh - ${props.primaryHeaderHeight + LOGS_FOOTER_HEIGHT}px )`,
+ },
+ }}
+ renderColumnActionsMenuItems={({ column }) => {
+ return ;
+ }}
+ />
+ {contextMenu.visible && (
+
+
+
+ )}
+ >
);
};
diff --git a/src/pages/Stream/hooks/useParamsController.ts b/src/pages/Stream/hooks/useParamsController.ts
index b7894003..2eccba5f 100644
--- a/src/pages/Stream/hooks/useParamsController.ts
+++ b/src/pages/Stream/hooks/useParamsController.ts
@@ -12,11 +12,11 @@ import { generateQueryBuilderASTFromSQL } from '../utils';
import { appStoreReducers, TimeRange, useAppStore } from '@/layouts/MainLayout/providers/AppProvider';
const { getRelativeStartAndEndDate, formatDateWithTimezone, getLocalTimezone } = timeRangeUtils;
-const { onToggleView, setPerPage, setCustQuerySearchState } = logsStoreReducers;
+const { onToggleView, setPerPage, setCustQuerySearchState, setRowNumber } = logsStoreReducers;
const { setTimeRange, syncTimeRange } = appStoreReducers;
const { applySavedFilters } = filterStoreReducers;
const timeRangeFormat = 'DD-MMM-YYYY_HH-mmz';
-const keys = ['view', 'rows', 'interval', 'from', 'to', 'query', 'filterType'];
+const keys = ['view', 'rows', 'interval', 'from', 'to', 'query', 'filterType', 'rowNumber'];
const dateToParamString = (date: Date) => {
return formatDateWithTimezone(date, timeRangeFormat);
@@ -52,6 +52,14 @@ const deriveTimeRangeParams = (timerange: TimeRange): { interval: string } | { f
}
};
+const deriveRowNumber = (rowNumber: number[]) => {
+ if (rowNumber.length > 0) {
+ return {
+ rowNumber: JSON.stringify(rowNumber),
+ };
+ }
+};
+
const storeToParamsObj = (opts: {
timeRange: TimeRange;
view: string;
@@ -60,10 +68,12 @@ const storeToParamsObj = (opts: {
rows: string;
query: string;
filterType: string;
+ rowNumber: number[];
}): Record => {
- const { timeRange, offset, page, view, rows, query, filterType } = opts;
+ const { timeRange, offset, page, view, rows, query, filterType, rowNumber } = opts;
const params: Record = {
...deriveTimeRangeParams(timeRange),
+ ...deriveRowNumber(rowNumber),
view,
offset,
rows,
@@ -94,7 +104,7 @@ const useParamsController = () => {
const [, setLogsStore] = useLogsStore((_store) => null);
const [, setFilterStore] = useFilterStore((store) => store);
- const { currentOffset, currentPage, perPage } = tableOpts;
+ const { currentOffset, currentPage, perPage, rowNumber } = tableOpts;
const [searchParams, setSearchParams] = useSearchParams();
@@ -107,6 +117,7 @@ const useParamsController = () => {
rows: `${perPage}`,
query: custQuerySearchState.custSearchQuery,
filterType: custQuerySearchState.viewMode,
+ rowNumber,
});
const presentParams = paramsStringToParamsObj(searchParams);
if (['table', 'json'].includes(presentParams.view) && presentParams.view !== storeAsParams.view) {
@@ -125,6 +136,7 @@ const useParamsController = () => {
);
}
syncTimeRangeToStore(storeAsParams, presentParams);
+ syncRowNumber(storeAsParams, presentParams);
setStoreSynced(true);
}, []);
@@ -138,6 +150,7 @@ const useParamsController = () => {
rows: `${perPage}`,
query: custQuerySearchState.custSearchQuery,
filterType: custQuerySearchState.viewMode,
+ rowNumber: rowNumber,
});
const presentParams = paramsStringToParamsObj(searchParams);
if (_.isEqual(storeAsParams, presentParams)) return;
@@ -156,6 +169,7 @@ const useParamsController = () => {
rows: `${perPage}`,
query: custQuerySearchState.custSearchQuery,
filterType: custQuerySearchState.viewMode,
+ rowNumber: rowNumber,
});
const presentParams = paramsStringToParamsObj(searchParams);
@@ -178,8 +192,17 @@ const useParamsController = () => {
setLogsStore((store) => setCustQuerySearchState(store, presentParams.query, presentParams.filterType));
}
syncTimeRangeToStore(storeAsParams, presentParams);
+ syncRowNumber(storeAsParams, presentParams);
}, [searchParams]);
+ const syncRowNumber = useCallback((storeAsParams: Record, presentParams: Record) => {
+ if (_.has(presentParams, 'rowNumber')) {
+ if (storeAsParams.rowNumber !== presentParams.rowNumber) {
+ setLogsStore((store) => setRowNumber(store, JSON.parse(presentParams.rowNumber)));
+ }
+ }
+ }, []);
+
const syncTimeRangeToStore = useCallback(
(storeAsParams: Record, presentParams: Record) => {
if (_.has(presentParams, 'interval')) {
diff --git a/src/pages/Stream/providers/LogsProvider.tsx b/src/pages/Stream/providers/LogsProvider.tsx
index 8af9ed18..7c0e54cc 100644
--- a/src/pages/Stream/providers/LogsProvider.tsx
+++ b/src/pages/Stream/providers/LogsProvider.tsx
@@ -187,6 +187,7 @@ type LogsStore = {
instantSearchValue: string;
configViewType: 'schema' | 'columns';
enableWordWrap: boolean;
+ rowNumber: number[];
};
data: LogQueryData;
@@ -251,6 +252,7 @@ type LogsStoreReducers = {
setDisabledColumns: (store: LogsStore, columns: string[]) => ReducerOutput;
setOrderedHeaders: (store: LogsStore, columns: string[]) => ReducerOutput;
toggleWordWrap: (store: LogsStore) => ReducerOutput;
+ setRowNumber: (store: LogsStore, rowNumber: number[]) => ReducerOutput;
};
const defaultSortKey = 'p_timestamp';
@@ -290,6 +292,7 @@ const initialState: LogsStore = {
instantSearchValue: '',
configViewType: 'columns',
enableWordWrap: true,
+ rowNumber: [],
},
// data
@@ -461,6 +464,16 @@ const togglePinnedColumns = (store: LogsStore, columnName: string) => {
};
};
+const setRowNumber = (store: LogsStore, rowNumber: number[]) => {
+ const { tableOpts } = store;
+ return {
+ tableOpts: {
+ ...tableOpts,
+ rowNumber,
+ },
+ };
+};
+
const filterAndSortData = (
opts: { sortOrder: 'asc' | 'desc'; sortKey: string; filters: Record },
data: Log[],
@@ -867,6 +880,7 @@ const logsStoreReducers: LogsStoreReducers = {
toggleDeleteModal,
toggleDisabledColumns,
togglePinnedColumns,
+ setRowNumber,
setLogData,
setStreamSchema,
setPerPage,
diff --git a/src/pages/Stream/styles/Logs.module.css b/src/pages/Stream/styles/Logs.module.css
index cd2308d8..bb08517f 100644
--- a/src/pages/Stream/styles/Logs.module.css
+++ b/src/pages/Stream/styles/Logs.module.css
@@ -274,3 +274,12 @@
}
}
}
+
+.contextMenuContainer {
+ position: fixed;
+ z-index: 1000;
+ background-color: white;
+ border: 1px solid #ccc;
+ border-radius: 4px;
+ box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
+}