From e08e116a83e2ff6e9e2fab11aa74bb0d0590914c Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Mon, 20 Jan 2025 11:08:11 +0530 Subject: [PATCH 01/23] feat: Log Count for correlation page --- src/api/query.ts | 8 ++- .../Correlation/Views/CorrelationFooter.tsx | 41 ++++++++++-- .../hooks/useCorrelationFetchCount.tsx | 63 +++++++++++++++++++ src/pages/Correlation/index.tsx | 7 ++- .../providers/CorrelationProvider.tsx | 11 ++++ src/utils/queryBuilder.ts | 6 ++ 6 files changed, 128 insertions(+), 8 deletions(-) create mode 100644 src/pages/Correlation/hooks/useCorrelationFetchCount.tsx diff --git a/src/api/query.ts b/src/api/query.ts index c9bc2c59..c477d219 100644 --- a/src/api/query.ts +++ b/src/api/query.ts @@ -68,6 +68,12 @@ export const getCorrelationQueryLogsWithHeaders = (logsQuery: CorrelationLogs) = return Axios().post(endPoint, queryBuilder.getCorrelationQuery(), {}); }; +export const getCorrelationQueryCount = (logsQuery: CorrelationLogs) => { + const queryBuilder = new CorrelationQueryBuilder(logsQuery); + const endPoint = LOG_QUERY_URL(); + return Axios().post(endPoint, makeCustomQueryRequestData(logsQuery, queryBuilder.getCountQuery()), {}); +}; + export const getStreamDataWithHeaders = (logsQuery: CorrelationLogs) => { const queryBuilder = new CorrelationQueryBuilder(logsQuery); const endPoint = LOG_QUERY_URL({ fields: true }, queryBuilder.getResourcePath()); @@ -76,7 +82,7 @@ export const getStreamDataWithHeaders = (logsQuery: CorrelationLogs) => { // ------ Custom sql query -const makeCustomQueryRequestData = (logsQuery: LogsQuery, query: string) => { +const makeCustomQueryRequestData = (logsQuery: LogsQuery | CorrelationLogs, query: string) => { const { startTime, endTime } = logsQuery; return { query, startTime: optimizeTime(startTime), endTime: optimizeTime(endTime) }; }; diff --git a/src/pages/Correlation/Views/CorrelationFooter.tsx b/src/pages/Correlation/Views/CorrelationFooter.tsx index 35f89e78..9000691d 100644 --- a/src/pages/Correlation/Views/CorrelationFooter.tsx +++ b/src/pages/Correlation/Views/CorrelationFooter.tsx @@ -1,6 +1,6 @@ import { FC, useCallback } from 'react'; import { usePagination } from '@mantine/hooks'; -import { Box, Center, Group, Menu, Pagination, Stack } from '@mantine/core'; +import { Box, Center, Group, Menu, Pagination, Stack, Tooltip } from '@mantine/core'; import _ from 'lodash'; import { Text } from '@mantine/core'; import { IconSelector } from '@tabler/icons-react'; @@ -9,9 +9,37 @@ import classes from '../styles/Footer.module.css'; import { LOGS_FOOTER_HEIGHT } from '@/constants/theme'; import { correlationStoreReducers, useCorrelationStore } from '@/pages/Correlation/providers/CorrelationProvider'; import { LOG_QUERY_LIMITS, LOAD_LIMIT } from '@/pages/Stream/providers/LogsProvider'; +import { HumanizeNumber } from '@/utils/formatBytes'; const { setCurrentOffset, setCurrentPage, setPageAndPageData } = correlationStoreReducers; +const TotalCount = (props: { totalCount: number }) => { + return ( + + {HumanizeNumber(props.totalCount)} + + ); +}; + +const TotalLogsCount = (props: { hasTableLoaded: boolean; isTableEmpty: boolean }) => { + const [{ totalCount, perPage, pageData }] = useCorrelationStore((store) => store.tableOpts); + const displayedCount = _.size(pageData); + const showingCount = displayedCount < perPage ? displayedCount : perPage; + if (typeof totalCount !== 'number' || typeof displayedCount !== 'number') return ; + + return ( + + {!props.isTableEmpty ? ( + <> + {`Showing ${showingCount} out of`} + + records + + ) : null} + + ); +}; + const LimitControl: FC = () => { const [opened, setOpened] = useMountedState(false); const [perPage, setCorrelationStore] = useCorrelationStore((store) => store.tableOpts.perPage); @@ -58,6 +86,7 @@ const LimitControl: FC = () => { const CorrelationFooter = (props: { loaded: boolean; hasNoData: boolean; isFetchingCount: boolean }) => { const [tableOpts, setCorrelationStore] = useCorrelationStore((store) => store.tableOpts); + const [isCorrelatedData] = useCorrelationStore((store) => store.isCorrelatedData); const { totalPages, currentOffset, currentPage, perPage, totalCount } = tableOpts; const onPageChange = useCallback((page: number) => { @@ -84,14 +113,14 @@ const CorrelationFooter = (props: { loaded: boolean; hasNoData: boolean; isFetch [currentOffset], ); + console.log('currentOffset', currentOffset); + // console.log(LOAD_LIMIT); + console.log('totalCount', totalCount); + return ( - {/* */} + {isCorrelatedData && } {props.loaded ? ( diff --git a/src/pages/Correlation/hooks/useCorrelationFetchCount.tsx b/src/pages/Correlation/hooks/useCorrelationFetchCount.tsx new file mode 100644 index 00000000..895dfcd6 --- /dev/null +++ b/src/pages/Correlation/hooks/useCorrelationFetchCount.tsx @@ -0,0 +1,63 @@ +import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; +import { + CORRELATION_LOAD_LIMIT, + correlationStoreReducers, + useCorrelationStore, +} from '../providers/CorrelationProvider'; +import { useState } from 'react'; +import { useQuery } from 'react-query'; +import { getCorrelationQueryCount } from '@/api/query'; +import _ from 'lodash'; +import { useStreamStore } from '@/pages/Stream/providers/StreamProvider'; + +const { setTotalCount } = correlationStoreReducers; + +export const useCorrelationFetchCount = () => { + const [timeRange] = useAppStore((store) => store.timeRange); + const [{ fields, tableOpts, selectedFields, correlationCondition }, setCorrelationData] = useCorrelationStore( + (store) => store, + ); + const [streamInfo] = useStreamStore((store) => store.info); + const timePartitionColumn = _.get(streamInfo, 'time_partition', 'p_timestamp'); + const [countLoading, setCountLoading] = useState(true); + + const streamNames = Object.keys(fields); + const defaultQueryOpts = { + startTime: timeRange.startTime, + endTime: timeRange.endTime, + limit: CORRELATION_LOAD_LIMIT, + pageOffset: tableOpts.currentOffset, + timePartitionColumn, + selectedFields: _.flatMap(selectedFields, (values, key) => _.map(values, (value) => `${key}.${value}`)) || [], + correlationCondition: correlationCondition, + }; + const queryOpts = { ...defaultQueryOpts, streamNames }; + + const { refetch: refetchCount } = useQuery( + ['fetchCount', defaultQueryOpts], + async () => { + setCountLoading(true); + const data = await getCorrelationQueryCount(queryOpts); + const count = _.first(data.data)?.count; + typeof count === 'number' && setCorrelationData((store) => setTotalCount(store, count)); + return data; + }, + { + // query for count should always hit the endpoint for parseable query + refetchOnWindowFocus: false, + retry: false, + enabled: false, + onSuccess: () => { + setCountLoading(false); + }, + onError: () => { + setCountLoading(false); + }, + }, + ); + + return { + countLoading, + refetchCount, + }; +}; diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index 952f1028..ecc2a0df 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -31,6 +31,7 @@ import { useCorrelationsQuery } from '@/hooks/useCorrelations'; import SavedCorrelationsButton from './components/SavedCorrelationsBtn'; import SavedCorrelationsModal from './components/SavedCorrelationsModal'; import SaveCorrelationModal from './components/SaveCorrelationModal'; +import { useCorrelationFetchCount } from './hooks/useCorrelationFetchCount'; const { changeStream, syncTimeRange } = appStoreReducers; const { @@ -78,6 +79,7 @@ const Correlation = () => { const { getCorrelationData, loading: logsLoading, error: errorMessage } = useCorrelationQueryLogs(); const { getFetchStreamData, loading: streamsLoading } = useFetchStreamData(); const { fetchCorrelations, getCorrelationByIdMutation } = useCorrelationsQuery(); + const { refetchCount, countLoading } = useCorrelationFetchCount(); // Local State const [searchText, setSearchText] = useState(''); @@ -149,6 +151,7 @@ const Correlation = () => { useEffect(() => { if (isCorrelatedData) { + refetchCount(); getCorrelationData(); } else { getFetchStreamData(); @@ -159,6 +162,7 @@ const Correlation = () => { updateCorrelationCondition(); if (activeCorrelation && correlationCondition && isSavedCorrelation) { setCorrelationData((store) => setIsCorrelatedFlag(store, true)); + refetchCount(); getCorrelationData(); } correlationCondition && setIsCorrelationEnabled(true); @@ -474,6 +478,7 @@ const Correlation = () => { onClick={() => { setCorrelationData((store) => setIsCorrelatedFlag(store, true)); setIsCorrelationEnabled(false); + refetchCount(); getCorrelationData(); }}> Correlate @@ -496,7 +501,7 @@ const Correlation = () => { {...{ errorMessage, logsLoading, streamsLoading, showTable, hasNoData }} primaryHeaderHeight={primaryHeaderHeight} /> - + )} {Object.keys(selectedFields).length === 0 && ( diff --git a/src/pages/Correlation/providers/CorrelationProvider.tsx b/src/pages/Correlation/providers/CorrelationProvider.tsx index ec99c772..fd0bf30b 100644 --- a/src/pages/Correlation/providers/CorrelationProvider.tsx +++ b/src/pages/Correlation/providers/CorrelationProvider.tsx @@ -90,6 +90,7 @@ type CorrelationStoreReducers = { toggleSaveCorrelationModal: (_store: CorrelationStore, val: boolean) => ReducerOutput; cleanCorrelationStore: (store: CorrelationStore) => ReducerOutput; setSavedCorrelationId: (store: CorrelationStore, id: string) => ReducerOutput; + setTotalCount: (store: CorrelationStore, count: number) => ReducerOutput; }; const initialState: CorrelationStore = { @@ -260,6 +261,15 @@ const cleanCorrelationStore = (store: CorrelationStore) => { }; }; +const setTotalCount = (store: CorrelationStore, totalCount: number) => { + return { + tableOpts: { + ...store.tableOpts, + totalCount, + }, + }; +}; + const toggleSaveCorrelationModal = (store: CorrelationStore, val: boolean) => { return { ...store, @@ -599,6 +609,7 @@ const correlationStoreReducers: CorrelationStoreReducers = { setActiveCorrelation, cleanCorrelationStore, setSavedCorrelationId, + setTotalCount, }; export { CorrelationProvider, useCorrelationStore, correlationStoreReducers }; diff --git a/src/utils/queryBuilder.ts b/src/utils/queryBuilder.ts index fbfef159..402d0558 100644 --- a/src/utils/queryBuilder.ts +++ b/src/utils/queryBuilder.ts @@ -142,6 +142,12 @@ export class CorrelationQueryBuilder { }; } + getCountQuery() { + return `WITH user_query_count as ( ${ + this.getCorrelationQuery().query + } )SELECT count(*) as count from user_query_count`; + } + getParseableQuery() { /* eslint-disable no-useless-escape */ const query = `SELECT * FROM \"${this.streamNames[0]}\" LIMIT ${this.limit}`; From 88802eb2f04583730b713fb90b7d583b345678b7 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Mon, 20 Jan 2025 14:50:13 +0530 Subject: [PATCH 02/23] Added page number and rows to query params --- src/api/query.ts | 1 + src/hooks/useCorrelationQueryLogs.tsx | 13 ++-- .../Correlation/Views/CorrelationFooter.tsx | 19 +++-- .../Correlation/hooks/useParamsController.ts | 73 +++++++++++++++++-- src/pages/Correlation/index.tsx | 34 +++++++-- .../providers/CorrelationProvider.tsx | 24 ++++++ src/utils/queryBuilder.ts | 19 +++-- 7 files changed, 149 insertions(+), 34 deletions(-) diff --git a/src/api/query.ts b/src/api/query.ts index c477d219..c2e57db5 100644 --- a/src/api/query.ts +++ b/src/api/query.ts @@ -18,6 +18,7 @@ type CorrelationLogs = { startTime: Date; endTime: Date; limit: number; + pageOffset: number; correlationCondition?: string; selectedFields?: string[]; }; diff --git a/src/hooks/useCorrelationQueryLogs.tsx b/src/hooks/useCorrelationQueryLogs.tsx index f8b55d7d..59acc794 100644 --- a/src/hooks/useCorrelationQueryLogs.tsx +++ b/src/hooks/useCorrelationQueryLogs.tsx @@ -12,6 +12,7 @@ import { import { notifyError } from '@/utils/notification'; import { useQuery } from 'react-query'; import { LogsResponseWithHeaders } from '@/@types/parseable/api/query'; +import { useState } from 'react'; const { setStreamData } = correlationStoreReducers; @@ -22,6 +23,7 @@ export const useCorrelationQueryLogs = () => { const [currentStream] = useAppStore((store) => store.currentStream); const timePartitionColumn = _.get(streamInfo, 'time_partition', 'p_timestamp'); const [timeRange] = useAppStore((store) => store.timeRange); + const [loadingState, setLoading] = useState(true); const [ { tableOpts: { currentOffset }, @@ -39,13 +41,10 @@ export const useCorrelationQueryLogs = () => { correlationCondition: correlationCondition, }; - const { - isLoading: logsLoading, - isRefetching: logsRefetching, - refetch: getCorrelationData, - } = useQuery( + const { refetch: getCorrelationData } = useQuery( ['fetch-logs', defaultQueryOpts], async () => { + setLoading(true); const queryOpts = { ...defaultQueryOpts, streamNames }; const response = await getCorrelationQueryLogsWithHeaders(queryOpts); return [response]; @@ -54,6 +53,7 @@ export const useCorrelationQueryLogs = () => { enabled: false, refetchOnWindowFocus: false, onSuccess: async (responses) => { + setLoading(false); responses.map((data: { data: LogsResponseWithHeaders }) => { const logs = data.data; const isInvalidResponse = _.isEmpty(logs) || _.isNil(logs); @@ -70,6 +70,7 @@ export const useCorrelationQueryLogs = () => { }); }, onError: (data: AxiosError) => { + setLoading(false); const errorMessage = data.response?.data as string; setError(_.isString(errorMessage) && !_.isEmpty(errorMessage) ? errorMessage : 'Failed to query logs'); }, @@ -78,7 +79,7 @@ export const useCorrelationQueryLogs = () => { return { error, - loading: logsLoading || logsRefetching, + loadingState, getCorrelationData, }; }; diff --git a/src/pages/Correlation/Views/CorrelationFooter.tsx b/src/pages/Correlation/Views/CorrelationFooter.tsx index 9000691d..2dbd34e3 100644 --- a/src/pages/Correlation/Views/CorrelationFooter.tsx +++ b/src/pages/Correlation/Views/CorrelationFooter.tsx @@ -1,4 +1,4 @@ -import { FC, useCallback } from 'react'; +import { FC, useCallback, useEffect } from 'react'; import { usePagination } from '@mantine/hooks'; import { Box, Center, Group, Menu, Pagination, Stack, Tooltip } from '@mantine/core'; import _ from 'lodash'; @@ -87,12 +87,17 @@ const LimitControl: FC = () => { const CorrelationFooter = (props: { loaded: boolean; hasNoData: boolean; isFetchingCount: boolean }) => { const [tableOpts, setCorrelationStore] = useCorrelationStore((store) => store.tableOpts); const [isCorrelatedData] = useCorrelationStore((store) => store.isCorrelatedData); - const { totalPages, currentOffset, currentPage, perPage, totalCount } = tableOpts; + const { totalPages, currentOffset, currentPage, perPage, totalCount, targetPage } = tableOpts; const onPageChange = useCallback((page: number) => { - setCorrelationStore((store) => setPageAndPageData(store, page)); + setCorrelationStore((store) => setPageAndPageData(store, page, perPage)); }, []); + useEffect(() => { + if (!props.loaded) return; + pagination.setPage(targetPage ? targetPage : 1); + }, [props.loaded]); + const pagination = usePagination({ total: totalPages ?? 1, initialPage: 1, onChange: onPageChange }); const onChangeOffset = useCallback( (key: 'prev' | 'next') => { @@ -113,14 +118,12 @@ const CorrelationFooter = (props: { loaded: boolean; hasNoData: boolean; isFetch [currentOffset], ); - console.log('currentOffset', currentOffset); - // console.log(LOAD_LIMIT); - console.log('totalCount', totalCount); - return ( - {isCorrelatedData && } + {isCorrelatedData && totalCount > 0 && ( + + )} {props.loaded ? ( diff --git a/src/pages/Correlation/hooks/useParamsController.ts b/src/pages/Correlation/hooks/useParamsController.ts index aeb4fb02..5383746f 100644 --- a/src/pages/Correlation/hooks/useParamsController.ts +++ b/src/pages/Correlation/hooks/useParamsController.ts @@ -7,12 +7,14 @@ import timeRangeUtils from '@/utils/timeRangeUtils'; import moment from 'moment-timezone'; import { appStoreReducers, TimeRange, useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; +import { getOffset } from '@/utils'; +import { LOG_QUERY_LIMITS } from '@/pages/Stream/providers/LogsProvider'; const { getRelativeStartAndEndDate, formatDateWithTimezone, getLocalTimezone } = timeRangeUtils; -const { setCorrelationId } = correlationStoreReducers; +const { setCorrelationId, setTargetPage, setCurrentOffset, setPerPage } = correlationStoreReducers; const { setTimeRange } = appStoreReducers; const timeRangeFormat = 'DD-MMM-YYYY_HH-mmz'; -const keys = ['id', 'interval', 'from', 'to']; +const keys = ['id', 'interval', 'from', 'to', 'page', 'rows']; const dateToParamString = (date: Date) => { return formatDateWithTimezone(date, timeRangeFormat); @@ -48,10 +50,17 @@ const deriveTimeRangeParams = (timerange: TimeRange): { interval: string } | { f } }; -const storeToParamsObj = (opts: { correlationId: string; timeRange: TimeRange }): Record => { - const { correlationId, timeRange } = opts; +const storeToParamsObj = (opts: { + correlationId: string; + timeRange: TimeRange; + rows: string; + page: string; +}): Record => { + const { correlationId, timeRange, page, rows } = opts; const params: Record = { id: correlationId, + rows, + page, ...deriveTimeRangeParams(timeRange), }; return _.pickBy(params, (val, key) => !_.isEmpty(val) && _.includes(keys, key)); @@ -71,39 +80,87 @@ const paramsStringToParamsObj = (searchParams: URLSearchParams): Record { const [isStoreSynced, setStoreSynced] = useState(false); const [{ correlationId }, setCorrelationStore] = useCorrelationStore((store) => store); + const [tableOpts] = useCorrelationStore((store) => store.tableOpts); const [timeRange, setAppStore] = useAppStore((store) => store.timeRange); const [searchParams, setSearchParams] = useSearchParams(); + const { currentOffset, currentPage, targetPage, perPage } = tableOpts; + + const pageOffset = Math.ceil(currentOffset / perPage); + useEffect(() => { - const storeAsParams = storeToParamsObj({ correlationId, timeRange }); + const storeAsParams = storeToParamsObj({ + correlationId, + timeRange, + rows: `${perPage}`, + page: `${targetPage ? targetPage : Math.ceil(currentPage + pageOffset)}`, + }); const presentParams = paramsStringToParamsObj(searchParams); if (storeAsParams.id !== presentParams.id) { setCorrelationStore((store) => setCorrelationId(store, presentParams.id)); } + if (storeAsParams.rows !== presentParams.rows && LOG_QUERY_LIMITS.includes(_.toNumber(presentParams.rows))) { + setCorrelationStore((store) => setPerPage(store, _.toNumber(presentParams.rows))); + } + if (storeAsParams.page !== presentParams.page && !_.isEmpty(presentParams.page)) { + setCorrelationStore((store) => setTargetPage(store, _.toNumber(presentParams.page))); + const offset = getOffset(_.toNumber(presentParams.page), _.toNumber(presentParams.rows)); + + if (offset > 0) { + setCorrelationStore((store) => setCurrentOffset(store, offset)); + + setCorrelationStore((store) => + setTargetPage( + store, + Math.abs(_.toNumber(presentParams.page) - Math.ceil(offset / _.toNumber(presentParams.rows))), + ), + ); + } + } syncTimeRangeToStore(storeAsParams, presentParams); setStoreSynced(true); }, []); useEffect(() => { if (isStoreSynced) { - const storeAsParams = storeToParamsObj({ correlationId, timeRange }); + const storeAsParams = storeToParamsObj({ + correlationId, + timeRange, + rows: `${perPage}`, + page: `${targetPage ? targetPage : Math.ceil(currentPage + pageOffset)}`, + }); const presentParams = paramsStringToParamsObj(searchParams); if (_.isEqual(storeAsParams, presentParams)) return; setSearchParams(storeAsParams); } - }, [correlationId, isStoreSynced, timeRange.startTime.toISOString(), timeRange.endTime.toISOString()]); + }, [ + correlationId, + isStoreSynced, + timeRange.startTime.toISOString(), + timeRange.endTime.toISOString(), + targetPage, + tableOpts, + ]); useEffect(() => { if (!isStoreSynced) return; - const storeAsParams = storeToParamsObj({ correlationId, timeRange }); + const storeAsParams = storeToParamsObj({ + correlationId, + timeRange, + rows: `${perPage}`, + page: `${targetPage ? targetPage : Math.ceil(currentPage + pageOffset)}`, + }); const presentParams = paramsStringToParamsObj(searchParams); if (_.isEqual(storeAsParams, presentParams)) return; if (storeAsParams.id !== presentParams.id) { setCorrelationStore((store) => setCorrelationId(store, presentParams.id)); } + if (storeAsParams.rows !== presentParams.rows && LOG_QUERY_LIMITS.includes(_.toNumber(presentParams.rows))) { + setCorrelationStore((store) => setPerPage(store, _.toNumber(presentParams.rows))); + } syncTimeRangeToStore(storeAsParams, presentParams); }, [searchParams]); diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index ecc2a0df..8c3044e3 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -43,6 +43,8 @@ const { toggleSaveCorrelationModal, setActiveCorrelation, setCorrelationId, + setPageAndPageData, + setTargetPage, } = correlationStoreReducers; const Correlation = () => { @@ -76,7 +78,7 @@ const Correlation = () => { []; const { isLoading: multipleSchemasLoading } = useGetMultipleStreamSchemas(streamsToFetch); - const { getCorrelationData, loading: logsLoading, error: errorMessage } = useCorrelationQueryLogs(); + const { getCorrelationData, loadingState, error: errorMessage } = useCorrelationQueryLogs(); const { getFetchStreamData, loading: streamsLoading } = useFetchStreamData(); const { fetchCorrelations, getCorrelationByIdMutation } = useCorrelationsQuery(); const { refetchCount, countLoading } = useCorrelationFetchCount(); @@ -99,6 +101,7 @@ const Correlation = () => { value: stream.name, label: stream.name, })) ?? []; + const { currentOffset, pageData, targetPage, currentPage } = tableOpts; // Effects useEffect(() => { @@ -149,6 +152,14 @@ const Correlation = () => { getFetchStreamData(); }, [isCorrelatedData]); + useEffect(() => { + if (isCorrelatedData) { + getCorrelationData(); + } else { + getFetchStreamData(); + } + }, [currentOffset]); + useEffect(() => { if (isCorrelatedData) { refetchCount(); @@ -217,10 +228,21 @@ const Correlation = () => { }, []); // View Flags - const hasContentLoaded = !schemaLoading && !logsLoading && !streamsLoading && !multipleSchemasLoading; - const hasNoData = hasContentLoaded && !errorMessage && tableOpts.pageData.length === 0; + const hasContentLoaded = !schemaLoading && !loadingState && !streamsLoading && !multipleSchemasLoading; + const hasNoData = hasContentLoaded && !errorMessage && pageData.length === 0; const showTable = hasContentLoaded && !hasNoData && !errorMessage; + useEffect(() => { + if (!showTable) return; + + if (targetPage) { + setCorrelationData((store) => setPageAndPageData(store, targetPage)); + if (currentPage === targetPage) { + setCorrelationData((store) => setTargetPage(store, undefined)); + } + } + }, [loadingState, currentPage]); + if (isLoading) return; return ( @@ -273,7 +295,7 @@ const Correlation = () => { }} /> - {logsLoading || schemaLoading || streamsLoading || multipleSchemasLoading ? ( + {loadingState || schemaLoading || streamsLoading || multipleSchemasLoading ? ( {Array.from({ length: 8 }).map((_, index) => ( @@ -339,7 +361,7 @@ const Correlation = () => { addStream(value)} data={streamData.filter((stream) => !streamNames.includes(stream.value))} isFirst={false} @@ -498,7 +520,7 @@ const Correlation = () => { {Object.keys(selectedFields).length > 0 && ( <> diff --git a/src/pages/Correlation/providers/CorrelationProvider.tsx b/src/pages/Correlation/providers/CorrelationProvider.tsx index fd0bf30b..55977fdf 100644 --- a/src/pages/Correlation/providers/CorrelationProvider.tsx +++ b/src/pages/Correlation/providers/CorrelationProvider.tsx @@ -59,6 +59,7 @@ type CorrelationStore = { displayedCount: number; currentPage: number; perPage: number; + targetPage: number | undefined; currentOffset: number; headers: string[]; orderedHeaders: string[]; @@ -91,6 +92,8 @@ type CorrelationStoreReducers = { cleanCorrelationStore: (store: CorrelationStore) => ReducerOutput; setSavedCorrelationId: (store: CorrelationStore, id: string) => ReducerOutput; setTotalCount: (store: CorrelationStore, count: number) => ReducerOutput; + setTargetPage: (store: CorrelationStore, target: number | undefined) => ReducerOutput; + setPerPage: (store: CorrelationStore, perPage: number) => ReducerOutput; }; const initialState: CorrelationStore = { @@ -112,6 +115,7 @@ const initialState: CorrelationStore = { pinnedColumns: [], pageData: [], perPage: 50, + targetPage: undefined, totalCount: 0, displayedCount: 0, totalPages: 0, @@ -291,6 +295,15 @@ const setCorrelations = (store: CorrelationStore, correlations: Correlation[]) = }; }; +const setPerPage = (store: CorrelationStore, perPage: number) => { + return { + tableOpts: { + ...store.tableOpts, + perPage, + }, + }; +}; + const setActiveCorrelation = (store: CorrelationStore, correlation: Correlation | null) => { return { ...store, @@ -485,6 +498,15 @@ const setCurrentOffset = (store: CorrelationStore, currentOffset: number) => { }; }; +const setTargetPage = (store: CorrelationStore, target: number | undefined) => { + return { + tableOpts: { + ...store.tableOpts, + targetPage: target ? target : undefined, + }, + }; +}; + const setCurrentPage = (store: CorrelationStore, currentPage: number) => { return { tableOpts: { @@ -610,6 +632,8 @@ const correlationStoreReducers: CorrelationStoreReducers = { cleanCorrelationStore, setSavedCorrelationId, setTotalCount, + setTargetPage, + setPerPage, }; export { CorrelationProvider, useCorrelationStore, correlationStoreReducers }; diff --git a/src/utils/queryBuilder.ts b/src/utils/queryBuilder.ts index 402d0558..1c1685ae 100644 --- a/src/utils/queryBuilder.ts +++ b/src/utils/queryBuilder.ts @@ -25,6 +25,7 @@ type CorrelationQueryBuilderType = { selectedFields?: string[]; startTime: Date; endTime: Date; + pageOffset: number; }; //! RESOURCE PATH CONSTANTS @@ -106,6 +107,7 @@ export class CorrelationQueryBuilder { selectedFields?: string[]; startTime: Date; endTime: Date; + pageOffset: number; constructor({ streamNames, @@ -114,6 +116,7 @@ export class CorrelationQueryBuilder { selectedFields, startTime, endTime, + pageOffset, }: CorrelationQueryBuilderType) { this.streamNames = streamNames; this.startTime = startTime; @@ -121,6 +124,7 @@ export class CorrelationQueryBuilder { this.limit = limit; this.correlationCondition = correlationCondition; this.selectedFields = selectedFields; + this.pageOffset = pageOffset; } getCorrelationQuery() { @@ -134,7 +138,7 @@ export class CorrelationQueryBuilder { }) .join(', ')} from \"${this.streamNames[0]}\" join \"${this.streamNames[1]}\" on ${ this.correlationCondition - } offset 0 LIMIT ${this.limit}`; + } OFFSET ${this.pageOffset} LIMIT ${this.limit}`; return { startTime: this.startTime, endTime: this.endTime, @@ -143,14 +147,17 @@ export class CorrelationQueryBuilder { } getCountQuery() { - return `WITH user_query_count as ( ${ - this.getCorrelationQuery().query - } )SELECT count(*) as count from user_query_count`; + const baseQuery = this.getCorrelationQuery()?.query; + if (!baseQuery) { + throw new Error('Base query is undefined. Unable to generate count query.'); + } + + const queryWithoutLimit = baseQuery.replace(/LIMIT \d+/i, '').trim(); + return `WITH user_query_count as ( ${queryWithoutLimit} ) SELECT count(*) as count FROM user_query_count`; } getParseableQuery() { - /* eslint-disable no-useless-escape */ - const query = `SELECT * FROM \"${this.streamNames[0]}\" LIMIT ${this.limit}`; + const query = `SELECT * FROM \"${this.streamNames[0]}\" OFFSET ${this.pageOffset} LIMIT ${this.limit}`; return { startTime: this.startTime, endTime: this.endTime, From cd6f2907fdf5e70966261fda9e2cf1000acc36a6 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Mon, 20 Jan 2025 16:25:59 +0530 Subject: [PATCH 03/23] fix: Saved correlations modal fields wrap --- .../components/SavedCorrelationItem.tsx | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/pages/Correlation/components/SavedCorrelationItem.tsx b/src/pages/Correlation/components/SavedCorrelationItem.tsx index e2240a8e..8ee32d87 100644 --- a/src/pages/Correlation/components/SavedCorrelationItem.tsx +++ b/src/pages/Correlation/components/SavedCorrelationItem.tsx @@ -1,6 +1,6 @@ import { Stack, Box, Button, Text, px, Code } from '@mantine/core'; import { IconClock, IconEye, IconEyeOff, IconTrash, IconX } from '@tabler/icons-react'; -import { useState, useCallback, Fragment, FC } from 'react'; +import { useState, useCallback, FC } from 'react'; import classes from '../styles/SavedCorrelationItem.module.css'; import { Correlation } from '@/@types/parseable/api/correlation'; import dayjs from 'dayjs'; @@ -51,14 +51,13 @@ const SelectedFields: FC<{ tableConfigs: TableConfig[] }> = ({ tableConfigs }) = ); return ( -
- Selected Fields: - {fields.map((field, index) => ( - +
+
Selected Fields:
+
+ {fields.map((field) => ( {field.content} - {index < fields.length - 1 && ,} - - ))} + ))} +
); }; @@ -73,8 +72,8 @@ const JoinConditions: FC<{ joinConfig: JoinConfig }> = ({ joinConfig }) => { if (!nextJoin) return null; return ( -
- Join Condition: +
+
Join Condition:
{`${join.tableName}.${join.field}`} = {`${nextJoin.tableName}.${nextJoin.field}`} @@ -154,7 +153,7 @@ const SavedCorrelationItem = (props: { item: Correlation }) => { {showQuery && ( - + From 2fb5d43a0a1f3e9d233b094fd142fd52058f92c2 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Mon, 20 Jan 2025 21:07:49 +0530 Subject: [PATCH 04/23] JSON view default --- .../Navbar/components/CorrelationIcon.tsx | 37 +++++- src/pages/Correlation/index.tsx | 44 ++++++- .../providers/CorrelationProvider.tsx | 123 +++++++++++++++++- .../Correlation/styles/Correlation.module.css | 22 ++++ src/pages/Stream/styles/JSONView.module.css | 1 + 5 files changed, 213 insertions(+), 14 deletions(-) diff --git a/src/components/Navbar/components/CorrelationIcon.tsx b/src/components/Navbar/components/CorrelationIcon.tsx index 1962aab1..1ff4b6e3 100644 --- a/src/components/Navbar/components/CorrelationIcon.tsx +++ b/src/components/Navbar/components/CorrelationIcon.tsx @@ -9,20 +9,45 @@ export const CorrelationIcon = forwardRef< >(({ stroke, strokeWidth }, ref) => ( + + + + // + // + // + // )); CorrelationIcon.displayName = 'CorrelationIcon'; diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index 8c3044e3..f2f08ee3 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import { useDocumentTitle } from '@mantine/hooks'; -import { Stack, Box, TextInput, Text, Select, Button, Center, Skeleton, Stepper } from '@mantine/core'; +import { Stack, Box, TextInput, Text, Select, Button, Center, Skeleton, Stepper, Pill } from '@mantine/core'; import { IconTrashX } from '@tabler/icons-react'; import { PRIMARY_HEADER_HEIGHT, @@ -32,6 +32,8 @@ import SavedCorrelationsButton from './components/SavedCorrelationsBtn'; import SavedCorrelationsModal from './components/SavedCorrelationsModal'; import SaveCorrelationModal from './components/SaveCorrelationModal'; import { useCorrelationFetchCount } from './hooks/useCorrelationFetchCount'; +import CorrleationJSONView from './Views/CorrelationJSONView'; +import ViewToggle from './components/CorrelationViewToggle'; const { changeStream, syncTimeRange } = appStoreReducers; const { @@ -61,6 +63,7 @@ const Correlation = () => { correlationCondition, correlationId, savedCorrelationId, + viewMode, }, setCorrelationData, ] = useCorrelationStore((store) => store); @@ -113,6 +116,7 @@ const Correlation = () => { useEffect(() => { if (multipleSchemasLoading || !activeCorrelation) return; + setCorrelationData((store) => setSelectedFields(store, '', '', true)); const tableOrder = activeCorrelation?.tableConfigs.reduce((acc, config, index) => { acc[config.tableName] = index; @@ -482,6 +486,28 @@ const Correlation = () => { + + + {isCorrelatedData && ( + + { + setSelect1Value(null); + setSelect2Value(null); + setCorrelationData((store) => setCorrelationCondition(store, '')); + setCorrelationData((store) => setIsCorrelatedFlag(store, false)); + setCorrelationData((store) => setCorrelationId(store, '')); + setCorrelationData((store) => setActiveCorrelation(store, null)); + setIsCorrelationEnabled(false); + setAppStore(syncTimeRange); + }}> + {correlationCondition?.split('=')[0]?.replace(/"/g, '').trim()} + = + {correlationCondition?.split('=')[1]?.replace(/"/g, '').trim()} + + + )}
+//
+// ); +// }; + +const TableContainer = (props: { children: ReactNode }) => { + return {props.children}; +}; + +const CorrleationJSONView = (props: { errorMessage: string | null; hasNoData: boolean; showTable: boolean }) => { + const [maximized] = useAppStore((store) => store.maximized); + const [contextMenu, setContextMenu] = useState({ + visible: false, + x: 0, + y: 0, + row: null, + }); + + const contextMenuRef = useRef(null); + const { errorMessage, hasNoData, showTable } = props; + const [isSearching] = useState(false); + const primaryHeaderHeight = !maximized + ? PRIMARY_HEADER_HEIGHT + STREAM_PRIMARY_TOOLBAR_CONTAINER_HEIGHT + STREAM_SECONDARY_TOOLBAR_HRIGHT + : 0; + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (contextMenuRef.current && !contextMenuRef.current.contains(event.target as Node)) { + closeContextMenu(); + } + }; + + if (contextMenu.visible) { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [contextMenu.visible]); + + const closeContextMenu = () => setContextMenu({ visible: false, x: 0, y: 0, row: null }); + + return ( + + {/* */} + {!errorMessage ? ( + showTable ? ( + + + + + + + + + {contextMenu.visible && ( +
+ )} +
+ ) : hasNoData ? ( + <> + + + ) : ( + + ) + ) : ( + + )} +
+ ); +}; + +export default CorrleationJSONView; diff --git a/src/pages/Correlation/components/CorrelationViewToggle.tsx b/src/pages/Correlation/components/CorrelationViewToggle.tsx new file mode 100644 index 00000000..b0f8779d --- /dev/null +++ b/src/pages/Correlation/components/CorrelationViewToggle.tsx @@ -0,0 +1,35 @@ +import { Button, rem } from '@mantine/core'; +import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; +import { useCallback } from 'react'; +import { IconTable } from '@tabler/icons-react'; +import classes from '../styles/Correlation.module.css'; + +const { onToggleView } = correlationStoreReducers; + +const ViewToggle = () => { + const [viewMode, setCorrelationStore] = useCorrelationStore((store) => store.viewMode); + const iconProps = { + style: { width: rem(20), height: rem(20), display: 'block' }, + stroke: 1.8, + }; + const onToggle = useCallback(() => { + setCorrelationStore((store) => onToggleView(store, viewMode === 'table' ? 'json' : 'table')); + }, [viewMode]); + + const isActive = viewMode === 'table'; + return ( + + ); +}; + +export default ViewToggle; From e0da498ad044857b734694747c3e38d0acc29c51 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Tue, 21 Jan 2025 10:58:30 +0530 Subject: [PATCH 06/23] Removed the getCorrelationById redundant call --- .../Navbar/components/CorrelationIcon.tsx | 12 +++--- src/hooks/useCorrelationQueryLogs.tsx | 3 +- src/hooks/useCorrelations.tsx | 40 +----------------- src/pages/Correlation/index.tsx | 42 ++++++++++--------- src/pages/Stream/hooks/useParamsController.ts | 2 +- 5 files changed, 34 insertions(+), 65 deletions(-) diff --git a/src/components/Navbar/components/CorrelationIcon.tsx b/src/components/Navbar/components/CorrelationIcon.tsx index 1ff4b6e3..4fceab45 100644 --- a/src/components/Navbar/components/CorrelationIcon.tsx +++ b/src/components/Navbar/components/CorrelationIcon.tsx @@ -12,23 +12,23 @@ export const CorrelationIcon = forwardRef< d="M13.3335 17.3335L14.6669 18.6669C15.0205 19.0205 15.5001 19.2192 16.0002 19.2192C16.5003 19.2192 16.9799 19.0205 17.3335 18.6669L22.6669 13.3335C23.0205 12.9799 23.2192 12.5003 23.2192 12.0002C23.2192 11.5001 23.0205 11.0205 22.6669 10.6669L17.3335 5.33353C16.9799 4.97991 16.5003 4.78125 16.0002 4.78125C15.5001 4.78125 15.0205 4.97991 14.6669 5.33353L9.33353 10.6669C8.97991 11.0205 8.78125 11.5001 8.78125 12.0002C8.78125 12.5003 8.97991 12.9799 9.33353 13.3335L10.6669 14.6669" stroke={stroke} strokeWidth={strokeWidth} - stroke-linecap="round" - stroke-linejoin="round" + strokeLinecap="round" + strokeLinejoin="round" /> diff --git a/src/hooks/useCorrelationQueryLogs.tsx b/src/hooks/useCorrelationQueryLogs.tsx index 59acc794..9fd05349 100644 --- a/src/hooks/useCorrelationQueryLogs.tsx +++ b/src/hooks/useCorrelationQueryLogs.tsx @@ -14,7 +14,7 @@ import { useQuery } from 'react-query'; import { LogsResponseWithHeaders } from '@/@types/parseable/api/query'; import { useState } from 'react'; -const { setStreamData } = correlationStoreReducers; +const { setStreamData, setIsCorrelatedFlag } = correlationStoreReducers; export const useCorrelationQueryLogs = () => { const [error, setError] = useMountedState(null); @@ -68,6 +68,7 @@ export const useCorrelationQueryLogs = () => { notifyError({ message: `${currentStream} doesn't have any fields` }); } }); + setCorrelationStore((store) => setIsCorrelatedFlag(store, true)); }, onError: (data: AxiosError) => { setLoading(false); diff --git a/src/hooks/useCorrelations.tsx b/src/hooks/useCorrelations.tsx index a4ccada2..83ddd2a6 100644 --- a/src/hooks/useCorrelations.tsx +++ b/src/hooks/useCorrelations.tsx @@ -1,18 +1,11 @@ import { useMutation, useQuery } from 'react-query'; import _ from 'lodash'; -import { - deleteSavedCorrelation, - getCorrelationById, - getCorrelations, - saveCorrelation, - updateCorrelation, -} from '@/api/correlations'; +import { deleteSavedCorrelation, getCorrelations, saveCorrelation, updateCorrelation } from '@/api/correlations'; import { correlationStoreReducers, useCorrelationStore } from '@/pages/Correlation/providers/CorrelationProvider'; import { notifyError, notifySuccess } from '@/utils/notification'; import { AxiosError, isAxiosError } from 'axios'; import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; -import dayjs from 'dayjs'; const { setCorrelations, @@ -22,7 +15,7 @@ const { cleanCorrelationStore, toggleSavedCorrelationsModal, } = correlationStoreReducers; -const { setTimeRange, syncTimeRange } = appStoreReducers; +const { syncTimeRange } = appStoreReducers; export const useCorrelationsQuery = () => { const [{ correlationId }, setCorrelatedStore] = useCorrelationStore((store) => store); const [, setAppStore] = useAppStore((store) => store); @@ -44,30 +37,6 @@ export const useCorrelationsQuery = () => { }, }); - const { - mutate: getCorrelationByIdMutation, - isError: fetchCorrelationIdError, - isSuccess: fetchCorrelationIdSuccess, - isLoading: fetchCorrelationIdLoading, - } = useMutation((correlationId: string) => getCorrelationById(correlationId), { - onSuccess: (data: any) => { - data.data.startTime && - data.data.endTime && - setAppStore((store) => - setTimeRange(store, { - startTime: dayjs(data.data.startTime), - endTime: dayjs(data.data.endTime), - type: 'custom', - }), - ); - setCorrelatedStore((store) => setCorrelationId(store, data.data.id)); - setCorrelatedStore((store) => setActiveCorrelation(store, data.data)); - }, - onError: () => { - notifyError({ message: 'Failed to fetch correlation' }); - }, - }); - const { mutate: deleteSavedCorrelationMutation, isLoading: isDeleting } = useMutation( (data: { correlationId: string; onSuccess?: () => void }) => deleteSavedCorrelation(data.correlationId), { @@ -143,11 +112,6 @@ export const useCorrelationsQuery = () => { deleteSavedCorrelationMutation, isDeleting, - fetchCorrelationIdError, - fetchCorrelationIdSuccess, - fetchCorrelationIdLoading, - getCorrelationByIdMutation, - saveCorrelationMutation, isCorrelationSaving, diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index f2f08ee3..ef53a1a8 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -34,8 +34,9 @@ import SaveCorrelationModal from './components/SaveCorrelationModal'; import { useCorrelationFetchCount } from './hooks/useCorrelationFetchCount'; import CorrleationJSONView from './Views/CorrelationJSONView'; import ViewToggle from './components/CorrelationViewToggle'; +import dayjs from 'dayjs'; -const { changeStream, syncTimeRange } = appStoreReducers; +const { changeStream, syncTimeRange, setTimeRange } = appStoreReducers; const { deleteStreamData, setSelectedFields, @@ -64,6 +65,7 @@ const Correlation = () => { correlationId, savedCorrelationId, viewMode, + correlations, }, setCorrelationData, ] = useCorrelationStore((store) => store); @@ -83,7 +85,7 @@ const Correlation = () => { const { getCorrelationData, loadingState, error: errorMessage } = useCorrelationQueryLogs(); const { getFetchStreamData, loading: streamsLoading } = useFetchStreamData(); - const { fetchCorrelations, getCorrelationByIdMutation } = useCorrelationsQuery(); + const { fetchCorrelations } = useCorrelationsQuery(); const { refetchCount, countLoading } = useCorrelationFetchCount(); // Local State @@ -116,7 +118,6 @@ const Correlation = () => { useEffect(() => { if (multipleSchemasLoading || !activeCorrelation) return; - setCorrelationData((store) => setSelectedFields(store, '', '', true)); const tableOrder = activeCorrelation?.tableConfigs.reduce((acc, config, index) => { acc[config.tableName] = index; @@ -143,8 +144,22 @@ const Correlation = () => { useEffect(() => { if (!isSavedCorrelation || !correlationId) return; - getCorrelationByIdMutation(correlationId); - }, [correlationId]); + const activeCorrelation = correlations?.find((correlation) => correlation.id === correlationId) || null; + activeCorrelation?.startTime && + activeCorrelation?.endTime && + setAppStore((store) => + setTimeRange(store, { + startTime: dayjs(activeCorrelation?.startTime), + endTime: dayjs(activeCorrelation?.endTime), + type: 'custom', + }), + ); + setSelect1Value(null); + setSelect2Value(null); + setCorrelationData((store) => setCorrelationCondition(store, '')); + setCorrelationData((store) => setSelectedFields(store, '', '', true)); + setCorrelationData((store) => setActiveCorrelation(store, activeCorrelation)); + }, [correlationId, correlations]); useEffect(() => { if (currentStream && streamNames.length > 0 && Object.keys(fields).includes(currentStream)) { @@ -162,21 +177,11 @@ const Correlation = () => { } else { getFetchStreamData(); } - }, [currentOffset]); - - useEffect(() => { - if (isCorrelatedData) { - refetchCount(); - getCorrelationData(); - } else { - getFetchStreamData(); - } - }, [timeRange]); + }, [currentOffset, timeRange]); useEffect(() => { updateCorrelationCondition(); if (activeCorrelation && correlationCondition && isSavedCorrelation) { - setCorrelationData((store) => setIsCorrelatedFlag(store, true)); refetchCount(); getCorrelationData(); } @@ -196,7 +201,6 @@ const Correlation = () => { const condition = `"${streamNames[0]}".${select1Value} = "${streamNames[1]}".${select2Value}`; setAppStore((store) => changeStream(store, 'correlatedStream')); setCorrelationData((store) => setCorrelationCondition(store, condition)); - setIsCorrelationEnabled(true); } }; @@ -488,8 +492,8 @@ const Correlation = () => { - {isCorrelatedData && ( - + {isCorrelationEnabled && ( + { diff --git a/src/pages/Stream/hooks/useParamsController.ts b/src/pages/Stream/hooks/useParamsController.ts index 12efc497..453a1ff4 100644 --- a/src/pages/Stream/hooks/useParamsController.ts +++ b/src/pages/Stream/hooks/useParamsController.ts @@ -167,7 +167,7 @@ const useParamsController = () => { setLogsStore((store) => setPerPage(store, _.toNumber(presentParams.rows))); } - if (storeAsParams.fields !== presentParams.fields) { + if (storeAsParams.fields !== presentParams.fields && !_.isEmpty(presentParams.fields)) { setLogsStore((store) => setTargetColumns(store, joinOrSplit(presentParams.fields) as string[])); } From d11deb11f0eaa644f6e6959c0af7cdf1763df173 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Tue, 21 Jan 2025 14:49:05 +0530 Subject: [PATCH 07/23] fix: Search for JSON View --- .../Navbar/components/CorrelationIcon.tsx | 62 ++---- src/hooks/useCorrelationQueryLogs.tsx | 2 +- .../Correlation/Views/CorrelationJSONView.tsx | 197 +++++++++--------- src/pages/Correlation/index.tsx | 10 +- .../providers/CorrelationProvider.tsx | 95 +++++---- 5 files changed, 177 insertions(+), 189 deletions(-) diff --git a/src/components/Navbar/components/CorrelationIcon.tsx b/src/components/Navbar/components/CorrelationIcon.tsx index 4fceab45..2541daec 100644 --- a/src/components/Navbar/components/CorrelationIcon.tsx +++ b/src/components/Navbar/components/CorrelationIcon.tsx @@ -7,47 +7,29 @@ export const CorrelationIcon = forwardRef< strokeWidth?: number; } >(({ stroke, strokeWidth }, ref) => ( - - - - - + + + + + + + - - // - // - // - // )); CorrelationIcon.displayName = 'CorrelationIcon'; diff --git a/src/hooks/useCorrelationQueryLogs.tsx b/src/hooks/useCorrelationQueryLogs.tsx index 9fd05349..9c7abbb2 100644 --- a/src/hooks/useCorrelationQueryLogs.tsx +++ b/src/hooks/useCorrelationQueryLogs.tsx @@ -63,12 +63,12 @@ export const useCorrelationQueryLogs = () => { if (fields.length > 0 && !correlationCondition) { return setCorrelationStore((store) => setStreamData(store, currentStream || '', records)); } else if (fields.length > 0 && correlationCondition) { + setCorrelationStore((store) => setIsCorrelatedFlag(store, true)); return setCorrelationStore((store) => setStreamData(store, 'correlatedStream', records)); } else { notifyError({ message: `${currentStream} doesn't have any fields` }); } }); - setCorrelationStore((store) => setIsCorrelatedFlag(store, true)); }, onError: (data: AxiosError) => { setLoading(false); diff --git a/src/pages/Correlation/Views/CorrelationJSONView.tsx b/src/pages/Correlation/Views/CorrelationJSONView.tsx index fa0da6bd..164ca7a5 100644 --- a/src/pages/Correlation/Views/CorrelationJSONView.tsx +++ b/src/pages/Correlation/Views/CorrelationJSONView.tsx @@ -1,4 +1,4 @@ -import { Box, Stack } from '@mantine/core'; +import { Box, Button, Loader, Stack, TextInput, Text } from '@mantine/core'; import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; import classes from '../../Stream/styles/JSONView.module.css'; import EmptyBox from '@/components/Empty'; @@ -10,12 +10,14 @@ import { } from '@/constants/theme'; import { Log } from '@/@types/parseable/api/query'; import _ from 'lodash'; -import { IconCheck, IconCopy, IconDotsVertical } from '@tabler/icons-react'; +import { IconCheck, IconCopy, IconDotsVertical, IconSearch } from '@tabler/icons-react'; import { copyTextToClipboard } from '@/utils'; import timeRangeUtils from '@/utils/timeRangeUtils'; -import { useCorrelationStore } from '../providers/CorrelationProvider'; +import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; import { ErrorView, LoadingView } from '@/pages/Stream/Views/Explore/LoadingViews'; import { formatLogTs, isJqSearch } from '@/pages/Stream/providers/LogsProvider'; +import jqSearch from '@/utils/jqSearch'; +import { useHotkeys } from '@mantine/hooks'; type ContextMenuState = { visible: boolean; @@ -24,7 +26,7 @@ type ContextMenuState = { row: Log | null; }; -// const { setInstantSearchValue, applyInstantSearch, applyJqSearch } = correlationStoreReducers; +const { setInstantSearchValue, applyInstantSearch, applyJqSearch } = correlationStoreReducers; const Item = (props: { header: string | null; value: string; highlight: boolean }) => { return ( @@ -171,105 +173,104 @@ const JsonRows = (props: { isSearching: boolean; setContextMenu: any }) => { ); }; -// const Toolbar = ({ -// isSearching, -// setSearching, -// }: { -// isSearching: boolean; -// setSearching: React.Dispatch>; -// }) => { -// const [localSearchValue, setLocalSearchValue] = useState(''); -// const searchInputRef = useRef(null); - -// const [searchValue, setCorrelationData] = useCorrelationStore((store) => store.tableOpts.instantSearchValue); -// const [{ rawData, filteredData }] = useCorrelationStore((store) => store.data); +const Toolbar = ({ + isSearching, + setSearching, +}: { + isSearching: boolean; + setSearching: React.Dispatch>; +}) => { + const [localSearchValue, setLocalSearchValue] = useState(''); + const searchInputRef = useRef(null); -// const debouncedSearch = useCallback( -// _.debounce(async (val: string) => { -// if (val.trim() === '') { -// setCorrelationData((store) => setInstantSearchValue(store, '')); -// setCorrelationData(applyInstantSearch); -// } else { -// const isJq = isJqSearch(val); -// if (isJq) { -// const jqResult = await jqSearch(rawData, val); -// setCorrelationData((store) => applyJqSearch(store, jqResult)); -// } else { -// setCorrelationData(applyInstantSearch); -// } -// } -// setSearching(false); -// }, 500), -// [rawData], -// ); + const [searchValue, setCorrelationData] = useCorrelationStore((store) => store.tableOpts.instantSearchValue); + const [pageData] = useCorrelationStore((store) => store.tableOpts); + const debouncedSearch = useCallback( + _.debounce(async (val: string) => { + if (val.trim() === '') { + setCorrelationData((store) => setInstantSearchValue(store, '')); + setCorrelationData(applyInstantSearch); + } else { + const isJq = isJqSearch(val); + if (isJq) { + const jqResult = await jqSearch(pageData, val); + setCorrelationData((store) => applyJqSearch(store, jqResult)); + } else { + setCorrelationData(applyInstantSearch); + } + } + setSearching(false); + }, 500), + [pageData], + ); -// const handleSearch = useCallback(() => { -// if (localSearchValue.trim()) { -// setSearching(true); -// setCorrelationData((store) => setInstantSearchValue(store, localSearchValue)); -// debouncedSearch(localSearchValue); -// } -// }, [localSearchValue, debouncedSearch, setSearching]); + const handleSearch = useCallback(() => { + if (localSearchValue.trim()) { + setSearching(true); + setCorrelationData((store) => setInstantSearchValue(store, localSearchValue)); + debouncedSearch(localSearchValue); + } + }, [localSearchValue, debouncedSearch, setSearching]); -// const handleInputChange = useCallback( -// (e: React.ChangeEvent) => { -// const value = e.target.value; -// setLocalSearchValue(value); -// if (value.trim() === '') { -// debouncedSearch(value); -// } -// }, -// [debouncedSearch], -// ); + const handleInputChange = useCallback( + (e: React.ChangeEvent) => { + const value = e.target.value; + setLocalSearchValue(value); + if (value.trim() === '') { + debouncedSearch(value); + } + }, + [debouncedSearch], + ); -// useHotkeys([['mod+K', () => searchInputRef.current?.focus()]]); + useHotkeys([['mod+K', () => searchInputRef.current?.focus()]]); -// const handleKeyDown = useCallback( -// (e: React.KeyboardEvent) => { -// if (e.key === 'Enter' && !isSearching && localSearchValue.trim()) { -// handleSearch(); -// } -// }, -// [isSearching, localSearchValue], -// ); + const handleKeyDown = useCallback( + (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !isSearching && localSearchValue.trim()) { + handleSearch(); + } + }, + [isSearching, localSearchValue], + ); -// if (_.isEmpty(rawData)) return null; + if (_.isEmpty(pageData)) return null; -// const inputStyles = { -// '--input-left-section-width': '2rem', -// '--input-right-section-width': '6rem', -// width: '100%', -// } as React.CSSProperties; + const inputStyles = { + '--input-left-section-width': '2rem', + '--input-right-section-width': '6rem', + width: '100%', + } as React.CSSProperties; -// return ( -//
-// : } -// placeholder="Search loaded data with text or jq. For jq input try `jq .[]`" -// value={localSearchValue} -// onChange={handleInputChange} -// onKeyDown={handleKeyDown} -// ref={searchInputRef} -// classNames={{ input: classes.inputField }} -// style={inputStyles} -// rightSection={ -// searchValue && !isSearching ? ( -// -// {filteredData.length} Matches -// -// ) : null -// } -// /> -// -//
-// ); -// }; + return ( +
+ : } + placeholder="Search loaded data with text or jq. For jq input try `jq .[]`" + value={localSearchValue} + onChange={handleInputChange} + onKeyDown={handleKeyDown} + ref={searchInputRef} + classNames={{ input: classes.inputField }} + style={inputStyles} + rightSection={ + searchValue && !isSearching ? ( + + Matches + + ) : null + } + /> + +
+ ); +}; const TableContainer = (props: { children: ReactNode }) => { return {props.children}; @@ -286,11 +287,13 @@ const CorrleationJSONView = (props: { errorMessage: string | null; hasNoData: bo const contextMenuRef = useRef(null); const { errorMessage, hasNoData, showTable } = props; - const [isSearching] = useState(false); + const [isSearching, setSearching] = useState(false); const primaryHeaderHeight = !maximized ? PRIMARY_HEADER_HEIGHT + STREAM_PRIMARY_TOOLBAR_CONTAINER_HEIGHT + STREAM_SECONDARY_TOOLBAR_HRIGHT : 0; + // const showTableOrLoader = logsLoading || streamsLoading || showTable || !errorMessage || !hasNoData; + useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (contextMenuRef.current && !contextMenuRef.current.contains(event.target as Node)) { @@ -311,7 +314,7 @@ const CorrleationJSONView = (props: { errorMessage: string | null; hasNoData: bo return ( - {/* */} + {!errorMessage ? ( showTable ? ( diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index ef53a1a8..32bec560 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -167,10 +167,6 @@ const Correlation = () => { } }, [currentStream, fields]); - useEffect(() => { - getFetchStreamData(); - }, [isCorrelatedData]); - useEffect(() => { if (isCorrelatedData) { getCorrelationData(); @@ -274,6 +270,8 @@ const Correlation = () => { const totalStreams = Object.entries(fields).length; const heightPercentage = totalStreams === 1 ? '50%' : `${100 / totalStreams}%`; + const isLoading = loadingState || schemaLoading || streamsLoading || multipleSchemasLoading; + if (!fieldsIter) return; return (
{ }} />
- {loadingState || schemaLoading || streamsLoading || multipleSchemasLoading ? ( + {isLoading ? ( {Array.from({ length: 8 }).map((_, index) => ( @@ -492,7 +490,7 @@ const Correlation = () => { - {isCorrelationEnabled && ( + {correlationCondition && ( ReducerOutput; setPerPage: (store: CorrelationStore, perPage: number) => ReducerOutput; onToggleView: (store: CorrelationStore, viewMode: 'json' | 'table') => ReducerOutput; - // applyJqSearch: (store: CorrelationStore, jqFilteredData: any[]) => ReducerOutput; - // applyInstantSearch: (store: CorrelationStore) => ReducerOutput; + applyJqSearch: (store: CorrelationStore, jqFilteredData: any[]) => ReducerOutput; + applyInstantSearch: (store: CorrelationStore) => ReducerOutput; setInstantSearchValue: (store: CorrelationStore, value: string) => ReducerOutput; }; @@ -115,6 +118,8 @@ const initialState: CorrelationStore = { activeCorrelation: null, isSavedCorrelationsModalOpen: false, isSaveCorrelationModalOpen: false, + filteredData: [], + rawData: [], viewMode: 'json', tableOpts: { disabledColumns: [], @@ -289,48 +294,47 @@ const searchAndSortData = (opts: { searchValue: string }, data: Log[]) => { }; // Reducer Functions +const applyJqSearch = (store: CorrelationStore, jqFilteredData: any[]) => { + const { tableOpts } = store; + const currentPage = 1; + const newPageSlice = getPageSlice(currentPage, tableOpts.perPage, jqFilteredData); + + return { + tableOpts: { + ...tableOpts, + filters: {}, + pageData: newPageSlice, + currentPage, + totalPages: getTotalPages(jqFilteredData, tableOpts.perPage), + }, + }; +}; + +const applyInstantSearch = (store: CorrelationStore) => { + const { tableOpts, rawData, selectedFields, streamData } = store; + const { instantSearchValue: searchValue, perPage } = tableOpts; + const filteredData = searchAndSortData({ searchValue }, rawData); + const currentPage = 1; + const newPageSlice = searchValue + ? getPageSlice(currentPage, tableOpts.perPage, filteredData) + : generatePaginatedPageData(store, selectedFields, currentPage, perPage); -// const applyJqSearch = (store: CorrelationStore, jqFilteredData: any[]) => { -// const { data, tableOpts } = store; -// const currentPage = 1; -// const newPageSlice = getPageSlice(currentPage, tableOpts.perPage, jqFilteredData); - -// return { -// data: { -// ...data, -// filteredData: jqFilteredData, -// }, -// tableOpts: { -// ...tableOpts, -// filters: {}, -// pageData: newPageSlice, -// currentPage, -// totalPages: getTotalPages(jqFilteredData, tableOpts.perPage), -// }, -// }; -// }; - -// const applyInstantSearch = (store: CorrelationStore) => { -// const { data, tableOpts } = store; -// const { instantSearchValue: searchValue } = tableOpts; -// const filteredData = searchAndSortData({ searchValue }, data.rawData); -// const currentPage = 1; -// const newPageSlice = getPageSlice(currentPage, tableOpts.perPage, filteredData); - -// return { -// data: { -// ...data, -// filteredData, -// }, -// tableOpts: { -// ...tableOpts, -// filters: {}, -// pageData: newPageSlice, -// currentPage, -// totalPages: getTotalPages(filteredData, tableOpts.perPage), -// }, -// }; -// }; + const totalPages = Math.max( + ...Object.values(streamData).map((stream) => + _.isEmpty(stream?.logData) ? 0 : Math.ceil(_.size(stream?.logData) / perPage), + ), + ); + return { + tableOpts: { + ...tableOpts, + filters: {}, + pageData: newPageSlice, + currentPage, + totalPages: searchValue ? getTotalPages(filteredData, tableOpts.perPage) : totalPages, + }, + filteredData: newPageSlice, + }; +}; const cleanCorrelationStore = (store: CorrelationStore) => { return { @@ -424,6 +428,7 @@ const setSelectedFields = ( return { ...store, selectedFields: updatedSelectedFields, + rawData: updatedPageData, tableOpts: { ...store.tableOpts, pageData: updatedPageData || [], @@ -748,8 +753,8 @@ const correlationStoreReducers: CorrelationStoreReducers = { setTargetPage, setPerPage, onToggleView, - // applyJqSearch, - // applyInstantSearch, + applyJqSearch, + applyInstantSearch, setInstantSearchValue, }; From b6be5e7efb0f9a400652a094c486345a68ee39a6 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Tue, 21 Jan 2025 16:32:13 +0530 Subject: [PATCH 08/23] Added badge for correlated data --- .../components/CorrelationFieldItem.tsx | 22 +-- .../components/CorrelationViewToggle.tsx | 6 +- src/pages/Correlation/index.tsx | 148 ++++++++++-------- .../Correlation/styles/Correlation.module.css | 7 +- 4 files changed, 92 insertions(+), 91 deletions(-) diff --git a/src/pages/Correlation/components/CorrelationFieldItem.tsx b/src/pages/Correlation/components/CorrelationFieldItem.tsx index 0119cebd..3926fb28 100644 --- a/src/pages/Correlation/components/CorrelationFieldItem.tsx +++ b/src/pages/Correlation/components/CorrelationFieldItem.tsx @@ -8,7 +8,7 @@ import { } from '@tabler/icons-react'; import { Text, Tooltip } from '@mantine/core'; import classes from '../styles/Correlation.module.css'; -import { useRef, useState, useEffect } from 'react'; +import { useRef } from 'react'; const dataTypeIcons = (iconColor: string): Record => ({ text: , @@ -40,13 +40,6 @@ export const CorrelationFieldItem = ({ onDelete, }: CorrelationFieldItemProps) => { const textRef = useRef(null); - const [isOverflowing, setIsOverflowing] = useState(false); - - useEffect(() => { - if (textRef.current) { - setIsOverflowing(textRef.current.scrollWidth > textRef.current.clientWidth); - } - }, [fieldName]); return (
- {isOverflowing ? ( - - - {fieldName} - - - ) : ( + {fieldName} - )} + + {!dataType && } {dataType && dataTypeIcons(iconColor)[dataType]}
diff --git a/src/pages/Correlation/components/CorrelationViewToggle.tsx b/src/pages/Correlation/components/CorrelationViewToggle.tsx index b0f8779d..11bd101f 100644 --- a/src/pages/Correlation/components/CorrelationViewToggle.tsx +++ b/src/pages/Correlation/components/CorrelationViewToggle.tsx @@ -2,7 +2,7 @@ import { Button, rem } from '@mantine/core'; import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; import { useCallback } from 'react'; import { IconTable } from '@tabler/icons-react'; -import classes from '../styles/Correlation.module.css'; +import classes from '../styles/SavedCorrelationsBtn.module.css'; const { onToggleView } = correlationStoreReducers; @@ -19,11 +19,11 @@ const ViewToggle = () => { const isActive = viewMode === 'table'; return ( + + +
@@ -481,64 +538,17 @@ const Correlation = () => { }}> {/* */}
- - - - - - - - - - {correlationCondition && ( - - { - setSelect1Value(null); - setSelect2Value(null); - setCorrelationData((store) => setCorrelationCondition(store, '')); - setCorrelationData((store) => setIsCorrelatedFlag(store, false)); - setCorrelationData((store) => setCorrelationId(store, '')); - setCorrelationData((store) => setActiveCorrelation(store, null)); - setIsCorrelationEnabled(false); - setAppStore(syncTimeRange); - }}> - {correlationCondition?.split('=')[0]?.replace(/"/g, '').trim()} - = - {correlationCondition?.split('=')[1]?.replace(/"/g, '').trim()} - - - )} -
-
- - - +
+ + + + +
+
+ + + +
diff --git a/src/pages/Correlation/styles/Correlation.module.css b/src/pages/Correlation/styles/Correlation.module.css index d7f8cfce..9b58053f 100644 --- a/src/pages/Correlation/styles/Correlation.module.css +++ b/src/pages/Correlation/styles/Correlation.module.css @@ -157,12 +157,13 @@ .fieldsPillsWrapper { width: 100%; - height: 100%; + height: auto; border-radius: rem(8px); display: flex; gap: 10px; - padding: 5px 8px; + padding: 5px; align-items: center; + flex-wrap: wrap; } .joinsWrapper { @@ -181,6 +182,8 @@ padding: 5px; height: 100%; margin-left: 44px; + width: 100%; + justify-content: space-between; } .clearBtn { From 0289abaf6a47f6906e244e3bbbb420e9cf63ecb9 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Tue, 21 Jan 2025 21:03:57 +0530 Subject: [PATCH 09/23] Added data type to join dropdown --- .../components/CorrelationFieldItem.tsx | 2 +- src/pages/Correlation/index.tsx | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/pages/Correlation/components/CorrelationFieldItem.tsx b/src/pages/Correlation/components/CorrelationFieldItem.tsx index 3926fb28..1a6559f1 100644 --- a/src/pages/Correlation/components/CorrelationFieldItem.tsx +++ b/src/pages/Correlation/components/CorrelationFieldItem.tsx @@ -10,7 +10,7 @@ import { Text, Tooltip } from '@mantine/core'; import classes from '../styles/Correlation.module.css'; import { useRef } from 'react'; -const dataTypeIcons = (iconColor: string): Record => ({ +export const dataTypeIcons = (iconColor: string): Record => ({ text: , timestamp: , number: , diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index 8450b3dd..64567c02 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import { useDocumentTitle } from '@mantine/hooks'; -import { Stack, Box, TextInput, Text, Select, Button, Center, Stepper, Badge } from '@mantine/core'; +import { Stack, Box, TextInput, Text, Select, Button, Center, Stepper, Badge, SelectProps, Group } from '@mantine/core'; import { IconTrashX, IconX } from '@tabler/icons-react'; import { PRIMARY_HEADER_HEIGHT, @@ -22,7 +22,7 @@ import RefreshNow from '@/components/Header/RefreshNow'; import MultiEventTimeLineGraph from './components/MultiEventTimeLineGraph'; import { CorrelationEmptyPlaceholder } from './components/CorrelationEmptyPlaceholder'; import { StreamSelectBox } from './components/StreamSelectBox'; -import { CorrelationFieldItem } from './components/CorrelationFieldItem'; +import { CorrelationFieldItem, dataTypeIcons } from './components/CorrelationFieldItem'; import { MaximizeButton } from '../Stream/components/PrimaryToolbar'; import ShareButton from './components/ShareButton'; import useParamsController from './hooks/useParamsController'; @@ -231,6 +231,26 @@ const Correlation = () => { setCorrelationData((store) => toggleSaveCorrelationModal(store, true)); }, []); + const renderJoinOneOptions: SelectProps['renderOption'] = ({ option }) => { + const fieldType = fields[streamNames[0]]?.fieldTypeMap[option.value]; + return ( +
+ {option.label} + {dataTypeIcons('black')[fieldType]} +
+ ); + }; + + const renderJoinTwoOptions: SelectProps['renderOption'] = ({ option }) => { + const fieldType = fields[streamNames[1]]?.fieldTypeMap[option.value]; + return ( +
+ {option.label} + {dataTypeIcons('black')[fieldType]} +
+ ); + }; + // View Flags const hasContentLoaded = !schemaLoading && !loadingState && !streamsLoading && !multipleSchemasLoading; const hasNoData = hasContentLoaded && !errorMessage && pageData.length === 0; @@ -442,6 +462,7 @@ const Correlation = () => { } value={select1Value} onChange={(value) => handleFieldChange(value, true)} + renderOption={renderJoinOneOptions} /> = @@ -462,6 +483,7 @@ const Correlation = () => { } value={select2Value} onChange={(value) => handleFieldChange(value, false)} + renderOption={renderJoinTwoOptions} />
From ed84f93d3de8ed203a3937c00475c12d174b46a6 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Tue, 21 Jan 2025 21:17:53 +0530 Subject: [PATCH 10/23] fix: Filter join dropdown based on seleciton --- src/pages/Correlation/index.tsx | 62 +++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index 64567c02..4507046f 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -1,6 +1,6 @@ import { useCallback, useEffect, useState } from 'react'; import { useDocumentTitle } from '@mantine/hooks'; -import { Stack, Box, TextInput, Text, Select, Button, Center, Stepper, Badge, SelectProps, Group } from '@mantine/core'; +import { Stack, Box, TextInput, Text, Select, Button, Center, Stepper, Badge, SelectProps } from '@mantine/core'; import { IconTrashX, IconX } from '@tabler/icons-react'; import { PRIMARY_HEADER_HEIGHT, @@ -90,8 +90,20 @@ const Correlation = () => { // Local State const [searchText, setSearchText] = useState(''); - const [select1Value, setSelect1Value] = useState(null); - const [select2Value, setSelect2Value] = useState(null); + const [select1Value, setSelect1Value] = useState<{ + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }>({ + value: null, + dataType: '', + }); + const [select2Value, setSelect2Value] = useState<{ + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }>({ + value: null, + dataType: '', + }); const [isCorrelationEnabled, setIsCorrelationEnabled] = useState(false); const [isLoading, setIsLoading] = useState(true); @@ -129,10 +141,10 @@ const Correlation = () => { ); if (sortedJoinConditions[0]) { - setSelect1Value(sortedJoinConditions[0].field); + setSelect1Value({ value: sortedJoinConditions[0].field }); } if (sortedJoinConditions[1]) { - setSelect2Value(sortedJoinConditions[1].field); + setSelect2Value({ value: sortedJoinConditions[1].field }); } activeCorrelation?.tableConfigs.flatMap((config) => @@ -154,8 +166,8 @@ const Correlation = () => { type: 'custom', }), ); - setSelect1Value(null); - setSelect2Value(null); + setSelect1Value({ value: null, dataType: '' }); + setSelect2Value({ value: null, dataType: '' }); setCorrelationData((store) => setCorrelationCondition(store, '')); setCorrelationData((store) => setSelectedFields(store, '', '', true)); setCorrelationData((store) => setActiveCorrelation(store, activeCorrelation)); @@ -193,8 +205,8 @@ const Correlation = () => { }; const updateCorrelationCondition = () => { - if (select1Value && select2Value) { - const condition = `"${streamNames[0]}".${select1Value} = "${streamNames[1]}".${select2Value}`; + if (select1Value.value && select2Value.value) { + const condition = `"${streamNames[0]}".${select1Value.value} = "${streamNames[1]}".${select2Value.value}`; setAppStore((store) => changeStream(store, 'correlatedStream')); setCorrelationData((store) => setCorrelationCondition(store, condition)); } @@ -209,15 +221,18 @@ const Correlation = () => { const handleFieldChange = (fieldValue: string | null, isFirstField: boolean) => { if (isFirstField) { - setSelect1Value(fieldValue); + const fieldType = fieldValue && fields[streamNames[0]]?.fieldTypeMap[fieldValue]; + console.log(fieldType); + + setSelect1Value({ value: fieldValue, dataType: fieldType }); } else { - setSelect2Value(fieldValue); + setSelect2Value({ value: fieldValue }); } }; const clearQuery = () => { - setSelect1Value(null); - setSelect2Value(null); + setSelect1Value({ value: null, dataType: '' }); + setSelect2Value({ value: null, dataType: '' }); setCorrelationData((store) => setCorrelationCondition(store, '')); setCorrelationData((store) => setSelectedFields(store, '', '', true)); setCorrelationData((store) => setIsCorrelatedFlag(store, false)); @@ -315,8 +330,8 @@ const Correlation = () => { onClick={() => { setAppStore((store) => changeStream(store, '')); setCorrelationData((store) => setIsCorrelatedFlag(store, false)); - setSelect1Value(null); - setSelect2Value(null); + setSelect1Value({ value: null, dataType: '' }); + setSelect2Value({ value: null, dataType: '' }); setCorrelationData((store) => deleteStreamData(store, stream)); }} /> @@ -460,7 +475,7 @@ const Correlation = () => { ) : [] } - value={select1Value} + value={select1Value.value} onChange={(value) => handleFieldChange(value, true)} renderOption={renderJoinOneOptions} /> @@ -476,12 +491,15 @@ const Correlation = () => { radius="md" data={ streamNames.length > 1 - ? Object.keys(fields[streamNames[1]].fieldTypeMap).filter( - (key) => fields[streamNames[1]].fieldTypeMap[key] !== 'list', - ) + ? Object.keys(fields[streamNames[1]].fieldTypeMap).filter((key) => { + const dataType = fields[streamNames[1]].fieldTypeMap[key]; + return ( + dataType !== 'list' && (!select1Value.dataType || select1Value.dataType === dataType) + ); + }) : [] } - value={select2Value} + value={select2Value.value} onChange={(value) => handleFieldChange(value, false)} renderOption={renderJoinTwoOptions} /> @@ -504,8 +522,8 @@ const Correlation = () => { size={12} color="#535BED" onClick={() => { - setSelect1Value(null); - setSelect2Value(null); + setSelect1Value({ value: null, dataType: '' }); + setSelect2Value({ value: null, dataType: '' }); setCorrelationData((store) => setCorrelationCondition(store, '')); setCorrelationData((store) => setIsCorrelatedFlag(store, false)); setCorrelationData((store) => setCorrelationId(store, '')); From 53c1f4d611dbd7c57ddc4523a9cc304f629e561e Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Wed, 22 Jan 2025 12:15:55 +0530 Subject: [PATCH 11/23] UI changes and Code refactoring --- .../components/CorrelationControlSection.tsx | 35 ++ .../components/CorrelationFieldsSection.tsx | 55 +++ .../components/CorrelationJoinSection.tsx | 221 +++++++++ .../components/CorrelationSaveIcon.tsx | 24 + .../components/CorrelationSideBar.tsx | 176 ++++++++ .../components/CorrelationToolbar.tsx | 52 +++ .../components/SavedCorrelationItem.tsx | 4 +- src/pages/Correlation/index.tsx | 423 +----------------- .../providers/CorrelationProvider.tsx | 10 +- .../Correlation/styles/Correlation.module.css | 8 + 10 files changed, 603 insertions(+), 405 deletions(-) create mode 100644 src/pages/Correlation/components/CorrelationControlSection.tsx create mode 100644 src/pages/Correlation/components/CorrelationFieldsSection.tsx create mode 100644 src/pages/Correlation/components/CorrelationJoinSection.tsx create mode 100644 src/pages/Correlation/components/CorrelationSaveIcon.tsx create mode 100644 src/pages/Correlation/components/CorrelationSideBar.tsx create mode 100644 src/pages/Correlation/components/CorrelationToolbar.tsx diff --git a/src/pages/Correlation/components/CorrelationControlSection.tsx b/src/pages/Correlation/components/CorrelationControlSection.tsx new file mode 100644 index 00000000..44d492bd --- /dev/null +++ b/src/pages/Correlation/components/CorrelationControlSection.tsx @@ -0,0 +1,35 @@ +import classes from '../styles/Correlation.module.css'; +import SavedCorrelationsButton from './SavedCorrelationsBtn'; +import TimeRange from '@/components/Header/TimeRange'; +import RefreshInterval from '@/components/Header/RefreshInterval'; +import RefreshNow from '@/components/Header/RefreshNow'; +import ViewToggle from './CorrelationViewToggle'; +import ShareButton from './ShareButton'; +import { MaximizeButton } from '@/pages/Stream/components/PrimaryToolbar'; + +export const CorrelationControlSection = () => { + return ( +
+ {/* */} +
+
+ + + + +
+
+ + + +
+
+
+ ); +}; diff --git a/src/pages/Correlation/components/CorrelationFieldsSection.tsx b/src/pages/Correlation/components/CorrelationFieldsSection.tsx new file mode 100644 index 00000000..a6f3ee02 --- /dev/null +++ b/src/pages/Correlation/components/CorrelationFieldsSection.tsx @@ -0,0 +1,55 @@ +import { FC } from 'react'; +import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; +import { CorrelationFieldItem } from './CorrelationFieldItem'; +import classes from '../styles/Correlation.module.css'; +import { Text } from '@mantine/core'; + +const { deleteSelectedField } = correlationStoreReducers; + +interface CorrelationFieldsSectionProps { + setIsCorrelationEnabled: (enabled: boolean) => void; +} + +export const CorrelationFieldsSection: FC = ({ setIsCorrelationEnabled }) => { + const [{ fields, selectedFields, isCorrelatedData }, setCorrelationData] = useCorrelationStore((store) => store); + const streamNames = Object.keys(fields); + + return ( +
+ 0 ? 'black' : '#CBCBCB', + }}> + Fields + +
0 ? '1px solid #CBCBCB' : '1px solid #e1e5e8', + backgroundColor: streamNames.length > 0 ? 'white' : '#F7F8F9', + }} + className={classes.fieldsPillsWrapper}> + {Object.keys(selectedFields).length < 1 && ( + + Click on fields to correlate + + )} + {Object.entries(selectedFields).map(([streamName, fieldsMap]: [any, any]) => + fieldsMap.map((field: any, index: any) => ( + { + isCorrelatedData && setIsCorrelationEnabled(true); + setCorrelationData((store) => deleteSelectedField(store, field, streamName)); + }} + /> + )), + )} +
+
+ ); +}; diff --git a/src/pages/Correlation/components/CorrelationJoinSection.tsx b/src/pages/Correlation/components/CorrelationJoinSection.tsx new file mode 100644 index 00000000..3300932b --- /dev/null +++ b/src/pages/Correlation/components/CorrelationJoinSection.tsx @@ -0,0 +1,221 @@ +import { FC, useCallback } from 'react'; +import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; +import { dataTypeIcons } from './CorrelationFieldItem'; +import classes from '../styles/Correlation.module.css'; +import { ActionIcon, Badge, Button, Select, SelectProps, Text } from '@mantine/core'; +import { STREAM_PRIMARY_TOOLBAR_HEIGHT } from '@/constants/theme'; +import { IconX } from '@tabler/icons-react'; +import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; +import { useCorrelationFetchCount } from '../hooks/useCorrelationFetchCount'; +import { useCorrelationQueryLogs } from '@/hooks/useCorrelationQueryLogs'; +import { CorrelationSaveIcon } from './CorrelationSaveIcon'; + +const { + toggleSaveCorrelationModal, + setCorrelationCondition, + setActiveCorrelation, + setSelectedFields, + setIsCorrelatedFlag, + setCorrelationId, +} = correlationStoreReducers; + +const { syncTimeRange } = appStoreReducers; + +interface CorrelationJoinSectionProps { + select1Value: { value: string | null; dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null }; + select2Value: { value: string | null; dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null }; + isCorrelationEnabled: boolean; + setIsCorrelationEnabled: (enabled: boolean) => void; + setSelect1Value: (value: { + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }) => void; + setSelect2Value: (value: { + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }) => void; +} + +export const CorrelationJoinSection: FC = ({ + setIsCorrelationEnabled, + setSelect1Value, + setSelect2Value, + select1Value, + select2Value, + isCorrelationEnabled, +}) => { + const [{ fields, selectedFields, isCorrelatedData }, setCorrelationData] = useCorrelationStore((store) => store); + const [, setAppStore] = useAppStore((store) => store); + const streamNames = Object.keys(fields); + const { refetchCount } = useCorrelationFetchCount(); + const { getCorrelationData } = useCorrelationQueryLogs(); + + const clearQuery = () => { + setSelect1Value({ value: null, dataType: '' }); + setSelect2Value({ value: null, dataType: '' }); + setCorrelationData((store) => setCorrelationCondition(store, '')); + setCorrelationData((store) => setSelectedFields(store, '', '', true)); + setCorrelationData((store) => setIsCorrelatedFlag(store, false)); + setCorrelationData((store) => setCorrelationId(store, '')); + setCorrelationData((store) => setActiveCorrelation(store, null)); + setIsCorrelationEnabled(false); + setAppStore(syncTimeRange); + }; + + const renderJoinOneOptions: SelectProps['renderOption'] = ({ option }) => { + const fieldType = fields[streamNames[0]]?.fieldTypeMap[option.value]; + return ( +
+ {option.label} + {dataTypeIcons('black')[fieldType]} +
+ ); + }; + + const renderJoinTwoOptions: SelectProps['renderOption'] = ({ option }) => { + const fieldType = fields[streamNames[1]]?.fieldTypeMap[option.value]; + return ( +
+ {option.label} + {dataTypeIcons('black')[fieldType]} +
+ ); + }; + + const handleFieldChange = (fieldValue: string | null, isFirstField: boolean) => { + if (isFirstField) { + const fieldType = fieldValue && fields[streamNames[0]]?.fieldTypeMap[fieldValue]; + console.log(fieldType); + + setSelect1Value({ value: fieldValue, dataType: fieldType }); + } else { + setSelect2Value({ value: fieldValue }); + } + }; + + const openSaveCorrelationModal = useCallback(() => { + setCorrelationData((store) => toggleSaveCorrelationModal(store, true)); + }, []); + + return ( +
+ 0 ? 'black' : '#CBCBCB', + flexShrink: 0, + flexGrow: 0, + }}> + Joins + +
+
+ 1 + ? Object.keys(fields[streamNames[1]].fieldTypeMap).filter((key) => { + const dataType = fields[streamNames[1]].fieldTypeMap[key]; + return dataType !== 'list' && (!select1Value.dataType || select1Value.dataType === dataType); + }) + : [] + } + value={select2Value.value} + onChange={(value) => handleFieldChange(value, false)} + renderOption={renderJoinTwoOptions} + /> +
+ { + openSaveCorrelationModal(); + }}> + + +
+ {isCorrelatedData && ( + { + setSelect1Value({ value: null, dataType: '' }); + setSelect2Value({ value: null, dataType: '' }); + setCorrelationData((store) => setCorrelationCondition(store, '')); + setCorrelationData((store) => setIsCorrelatedFlag(store, false)); + setCorrelationData((store) => setCorrelationId(store, '')); + setCorrelationData((store) => setActiveCorrelation(store, null)); + setIsCorrelationEnabled(false); + setAppStore(syncTimeRange); + }} + /> + }> + Join Applied + + )} +
+
+ + +
+
+
+ ); +}; diff --git a/src/pages/Correlation/components/CorrelationSaveIcon.tsx b/src/pages/Correlation/components/CorrelationSaveIcon.tsx new file mode 100644 index 00000000..cd7f959e --- /dev/null +++ b/src/pages/Correlation/components/CorrelationSaveIcon.tsx @@ -0,0 +1,24 @@ +import { px } from '@mantine/core'; + +export const CorrelationSaveIcon = () => ( + + + + + + +); + +CorrelationSaveIcon.displayName = 'CorrelationSaveIcon'; diff --git a/src/pages/Correlation/components/CorrelationSideBar.tsx b/src/pages/Correlation/components/CorrelationSideBar.tsx new file mode 100644 index 00000000..4955a8cb --- /dev/null +++ b/src/pages/Correlation/components/CorrelationSideBar.tsx @@ -0,0 +1,176 @@ +import { TextInput, Text } from '@mantine/core'; +import { IconTrashX } from '@tabler/icons-react'; +import classes from '../styles/Correlation.module.css'; +import { CorrelationFieldItem } from './CorrelationFieldItem'; +import { StreamSelectBox } from './StreamSelectBox'; +import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; +import { FC, useState } from 'react'; +import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; + +const { changeStream } = appStoreReducers; +const { setIsCorrelatedFlag, setSelectedFields, deleteStreamData } = correlationStoreReducers; + +interface CorrelationSideBarProps { + setIsCorrelationEnabled: (enabled: boolean) => void; + setSelect1Value: (value: { + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }) => void; + setSelect2Value: (value: { + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }) => void; + loadingState: boolean; + isLoading: boolean; +} + +export const CorrelationSidebar: FC = ({ + setIsCorrelationEnabled, + setSelect1Value, + setSelect2Value, + loadingState, + isLoading, +}) => { + const [searchText, setSearchText] = useState(''); + + const [{ fields, selectedFields, isCorrelatedData }, setCorrelationData] = useCorrelationStore((store) => store); + const [userSpecificStreams, setAppStore] = useAppStore((store) => store.userSpecificStreams); + const streamNames = Object.keys(fields); + const streamData = + userSpecificStreams?.map((stream: any) => ({ + value: stream.name, + label: stream.name, + })) ?? []; + + const filterFields = (fieldsIter: any) => { + const typedFields = Object.keys(fieldsIter.fieldTypeMap) as string[]; + return searchText + ? typedFields.filter((field) => field.toLowerCase().includes(searchText.toLowerCase())) + : typedFields; + }; + const addStream = (value: string | null) => { + if (value) { + setAppStore((store) => changeStream(store, value)); + } + }; + + return ( +
+ Streams + setSearchText(e.target.value)} + /> +
+ {Object.entries(fields).map(([stream, fieldsIter]: [any, any]) => { + if (!fieldsIter) return; + const filteredFields = filterFields(fieldsIter); + const totalStreams = Object.entries(fields).length; + const heightPercentage = totalStreams === 1 ? '50%' : `${100 / totalStreams}%`; + + return ( +
+
+ + {stream} + + { + setAppStore((store) => changeStream(store, '')); + setCorrelationData((store) => setIsCorrelatedFlag(store, false)); + setSelect1Value({ value: null, dataType: '' }); + setSelect2Value({ value: null, dataType: '' }); + setCorrelationData((store) => deleteStreamData(store, stream)); + setIsCorrelationEnabled(false); + }} + /> +
+ {filteredFields.length > 0 ? ( +
+ {filteredFields.map((field: string) => { + const isSelected = selectedFields[stream]?.includes(field); + const dataType = fieldsIter.fieldTypeMap[field]; + return ( + { + if (isLoading) return; + if (isCorrelatedData) { + setIsCorrelationEnabled(true); + } + setCorrelationData((store) => setSelectedFields(store, field, stream)); + }} + /> + ); + })} +
+ ) : ( + No fields match your search. + )} +
+ ); + })} + {streamNames.length === 0 && ( + <> + {/* First box */} + value && addStream(value)} + data={streamData.filter((stream) => !streamNames.includes(stream.value))} + isFirst={true} + /> + + {/* Second box */} + addStream(value)} + data={streamData.filter((stream) => !streamNames.includes(stream.value))} + isFirst={false} + /> + + )} + {streamNames.length === 1 && ( + <> + {/* Render the single existing field */} + addStream(value)} + data={streamData.filter((stream) => !streamNames.includes(stream.value))} + isFirst={false} + /> + + )} +
+
+ ); +}; diff --git a/src/pages/Correlation/components/CorrelationToolbar.tsx b/src/pages/Correlation/components/CorrelationToolbar.tsx new file mode 100644 index 00000000..62f7fabd --- /dev/null +++ b/src/pages/Correlation/components/CorrelationToolbar.tsx @@ -0,0 +1,52 @@ +import { Stack } from '@mantine/core'; +import { STREAM_SECONDARY_TOOLBAR_HRIGHT } from '@/constants/theme'; +import classes from '../styles/Correlation.module.css'; +import MultiEventTimeLineGraph from './MultiEventTimeLineGraph'; +import { CorrelationFieldsSection } from './CorrelationFieldsSection'; +import { FC } from 'react'; +import { CorrelationJoinSection } from './CorrelationJoinSection'; +import { CorrelationControlSection } from './CorrelationControlSection'; + +interface CorrelationToolbarProps { + setIsCorrelationEnabled: (enabled: boolean) => void; + select1Value: { value: string | null; dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null }; + select2Value: { value: string | null; dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null }; + isCorrelationEnabled: boolean; + setSelect1Value: (value: { + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }) => void; + setSelect2Value: (value: { + value: string | null; + dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; + }) => void; +} + +export const CorrelationToolbar: FC = ({ + setIsCorrelationEnabled, + setSelect1Value, + setSelect2Value, + select1Value, + select2Value, + isCorrelationEnabled, +}) => { + return ( + + + + + + + + + + + ); +}; diff --git a/src/pages/Correlation/components/SavedCorrelationItem.tsx b/src/pages/Correlation/components/SavedCorrelationItem.tsx index 8ee32d87..318ffd48 100644 --- a/src/pages/Correlation/components/SavedCorrelationItem.tsx +++ b/src/pages/Correlation/components/SavedCorrelationItem.tsx @@ -54,8 +54,8 @@ const SelectedFields: FC<{ tableConfigs: TableConfig[] }> = ({ tableConfigs }) =
Selected Fields:
- {fields.map((field) => ( - {field.content} + {fields.map((field, index) => ( + {field.content} ))}
diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index 4507046f..ab595533 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -1,11 +1,9 @@ -import { useCallback, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useDocumentTitle } from '@mantine/hooks'; -import { Stack, Box, TextInput, Text, Select, Button, Center, Stepper, Badge, SelectProps } from '@mantine/core'; -import { IconTrashX, IconX } from '@tabler/icons-react'; +import { Stack, Box, Center, Stepper } from '@mantine/core'; import { PRIMARY_HEADER_HEIGHT, STREAM_PRIMARY_TOOLBAR_CONTAINER_HEIGHT, - STREAM_PRIMARY_TOOLBAR_HEIGHT, STREAM_SECONDARY_TOOLBAR_HRIGHT, } from '@/constants/theme'; import classes from './styles/Correlation.module.css'; @@ -16,44 +14,25 @@ import { correlationStoreReducers, useCorrelationStore } from './providers/Corre import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; import CorrelationTable from './Views/CorrelationTable'; import CorrelationFooter from './Views/CorrelationFooter'; -import TimeRange from '@/components/Header/TimeRange'; -import RefreshInterval from '@/components/Header/RefreshInterval'; -import RefreshNow from '@/components/Header/RefreshNow'; -import MultiEventTimeLineGraph from './components/MultiEventTimeLineGraph'; import { CorrelationEmptyPlaceholder } from './components/CorrelationEmptyPlaceholder'; -import { StreamSelectBox } from './components/StreamSelectBox'; -import { CorrelationFieldItem, dataTypeIcons } from './components/CorrelationFieldItem'; -import { MaximizeButton } from '../Stream/components/PrimaryToolbar'; -import ShareButton from './components/ShareButton'; import useParamsController from './hooks/useParamsController'; import _ from 'lodash'; import { useCorrelationsQuery } from '@/hooks/useCorrelations'; -import SavedCorrelationsButton from './components/SavedCorrelationsBtn'; import SavedCorrelationsModal from './components/SavedCorrelationsModal'; import SaveCorrelationModal from './components/SaveCorrelationModal'; import { useCorrelationFetchCount } from './hooks/useCorrelationFetchCount'; import CorrleationJSONView from './Views/CorrelationJSONView'; -import ViewToggle from './components/CorrelationViewToggle'; import dayjs from 'dayjs'; +import { CorrelationToolbar } from './components/CorrelationToolbar'; +import { CorrelationSidebar } from './components/CorrelationSideBar'; -const { changeStream, syncTimeRange, setTimeRange } = appStoreReducers; -const { - deleteStreamData, - setSelectedFields, - deleteSelectedField, - setCorrelationCondition, - setIsCorrelatedFlag, - toggleSaveCorrelationModal, - setActiveCorrelation, - setCorrelationId, - setPageAndPageData, - setTargetPage, -} = correlationStoreReducers; +const { changeStream, setTimeRange } = appStoreReducers; +const { setSelectedFields, setCorrelationCondition, setActiveCorrelation, setPageAndPageData, setTargetPage } = + correlationStoreReducers; const Correlation = () => { useDocumentTitle('Parseable | Correlation'); // State Management Hooks - const [userSpecificStreams] = useAppStore((store) => store.userSpecificStreams); const [ { fields, @@ -89,7 +68,6 @@ const Correlation = () => { const { refetchCount, countLoading } = useCorrelationFetchCount(); // Local State - const [searchText, setSearchText] = useState(''); const [select1Value, setSelect1Value] = useState<{ value: string | null; dataType?: '' | 'number' | 'boolean' | 'text' | 'timestamp' | 'list' | null; @@ -113,11 +91,6 @@ const Correlation = () => { : PRIMARY_HEADER_HEIGHT + STREAM_PRIMARY_TOOLBAR_CONTAINER_HEIGHT + STREAM_SECONDARY_TOOLBAR_HRIGHT; const streamNames = Object.keys(fields); - const streamData = - userSpecificStreams?.map((stream: any) => ({ - value: stream.name, - label: stream.name, - })) ?? []; const { currentOffset, pageData, targetPage, currentPage } = tableOpts; // Effects @@ -196,14 +169,6 @@ const Correlation = () => { correlationCondition && setIsCorrelationEnabled(true); }, [select1Value, select2Value, activeCorrelation, correlationCondition]); - // Utility Functions - const filterFields = (fieldsIter: any) => { - const typedFields = Object.keys(fieldsIter.fieldTypeMap) as string[]; - return searchText - ? typedFields.filter((field) => field.toLowerCase().includes(searchText.toLowerCase())) - : typedFields; - }; - const updateCorrelationCondition = () => { if (select1Value.value && select2Value.value) { const condition = `"${streamNames[0]}".${select1Value.value} = "${streamNames[1]}".${select2Value.value}`; @@ -212,65 +177,13 @@ const Correlation = () => { } }; - // Event Handlers - const addStream = (value: string | null) => { - if (value) { - setAppStore((store) => changeStream(store, value)); - } - }; - - const handleFieldChange = (fieldValue: string | null, isFirstField: boolean) => { - if (isFirstField) { - const fieldType = fieldValue && fields[streamNames[0]]?.fieldTypeMap[fieldValue]; - console.log(fieldType); - - setSelect1Value({ value: fieldValue, dataType: fieldType }); - } else { - setSelect2Value({ value: fieldValue }); - } - }; - - const clearQuery = () => { - setSelect1Value({ value: null, dataType: '' }); - setSelect2Value({ value: null, dataType: '' }); - setCorrelationData((store) => setCorrelationCondition(store, '')); - setCorrelationData((store) => setSelectedFields(store, '', '', true)); - setCorrelationData((store) => setIsCorrelatedFlag(store, false)); - setCorrelationData((store) => setCorrelationId(store, '')); - setCorrelationData((store) => setActiveCorrelation(store, null)); - setIsCorrelationEnabled(false); - setAppStore(syncTimeRange); - }; - const openSaveCorrelationModal = useCallback((e: React.MouseEvent) => { - e.stopPropagation(); - setCorrelationData((store) => toggleSaveCorrelationModal(store, true)); - }, []); - - const renderJoinOneOptions: SelectProps['renderOption'] = ({ option }) => { - const fieldType = fields[streamNames[0]]?.fieldTypeMap[option.value]; - return ( -
- {option.label} - {dataTypeIcons('black')[fieldType]} -
- ); - }; - - const renderJoinTwoOptions: SelectProps['renderOption'] = ({ option }) => { - const fieldType = fields[streamNames[1]]?.fieldTypeMap[option.value]; - return ( -
- {option.label} - {dataTypeIcons('black')[fieldType]} -
- ); - }; - // View Flags const hasContentLoaded = !schemaLoading && !loadingState && !streamsLoading && !multipleSchemasLoading; const hasNoData = hasContentLoaded && !errorMessage && pageData.length === 0; const showTable = hasContentLoaded && !hasNoData && !errorMessage; + const isStreamsLoading = loadingState || schemaLoading || streamsLoading || multipleSchemasLoading; + useEffect(() => { if (!showTable) return; @@ -288,313 +201,25 @@ const Correlation = () => { -
- Streams - setSearchText(e.target.value)} - /> -
- {Object.entries(fields).map(([stream, fieldsIter]: [any, any]) => { - if (!fieldsIter) return; - const filteredFields = filterFields(fieldsIter); - const totalStreams = Object.entries(fields).length; - const heightPercentage = totalStreams === 1 ? '50%' : `${100 / totalStreams}%`; - - const isLoading = loadingState || schemaLoading || streamsLoading || multipleSchemasLoading; - return ( -
-
- - {stream} - - { - setAppStore((store) => changeStream(store, '')); - setCorrelationData((store) => setIsCorrelatedFlag(store, false)); - setSelect1Value({ value: null, dataType: '' }); - setSelect2Value({ value: null, dataType: '' }); - setCorrelationData((store) => deleteStreamData(store, stream)); - }} - /> -
- {filteredFields.length > 0 ? ( -
- {filteredFields.map((field: string) => { - const isSelected = selectedFields[stream]?.includes(field); - const dataType = fieldsIter.fieldTypeMap[field]; - return ( - { - if (isLoading) return; - if (isCorrelatedData) { - setIsCorrelationEnabled(true); - setCorrelationData((store) => setIsCorrelatedFlag(store, false)); - } - setCorrelationData((store) => setSelectedFields(store, field, stream)); - }} - /> - ); - })} -
- ) : ( - No fields match your search. - )} -
- ); - })} - {streamNames.length === 0 && ( - <> - {/* First box */} - value && addStream(value)} - data={streamData.filter((stream) => !streamNames.includes(stream.value))} - isFirst={true} - /> - - {/* Second box */} - addStream(value)} - data={streamData.filter((stream) => !streamNames.includes(stream.value))} - isFirst={false} - /> - - )} - {streamNames.length === 1 && ( - <> - {/* Render the single existing field */} - addStream(value)} - data={streamData.filter((stream) => !streamNames.includes(stream.value))} - isFirst={false} - /> - - )} -
-
+ - - -
- 0 ? 'black' : '#CBCBCB', - }}> - Fields - -
0 ? '1px solid #CBCBCB' : '1px solid #e1e5e8', - backgroundColor: streamNames.length > 0 ? 'white' : '#F7F8F9', - }} - className={classes.fieldsPillsWrapper}> - {Object.keys(selectedFields).length < 1 && ( - - Click on fields to correlate - - )} - {Object.entries(selectedFields).map(([streamName, fieldsMap]: [any, any]) => - fieldsMap.map((field: any, index: any) => ( - { - isCorrelatedData && setIsCorrelationEnabled(true); - setCorrelationData((store) => deleteSelectedField(store, field, streamName)); - }} - /> - )), - )} -
-
-
- 0 ? 'black' : '#CBCBCB', - flexShrink: 0, - flexGrow: 0, - }}> - Joins - -
-
- 1 - ? Object.keys(fields[streamNames[1]].fieldTypeMap).filter((key) => { - const dataType = fields[streamNames[1]].fieldTypeMap[key]; - return ( - dataType !== 'list' && (!select1Value.dataType || select1Value.dataType === dataType) - ); - }) - : [] - } - value={select2Value.value} - onChange={(value) => handleFieldChange(value, false)} - renderOption={renderJoinTwoOptions} - /> -
-
- {isCorrelatedData && ( - { - setSelect1Value({ value: null, dataType: '' }); - setSelect2Value({ value: null, dataType: '' }); - setCorrelationData((store) => setCorrelationCondition(store, '')); - setCorrelationData((store) => setIsCorrelatedFlag(store, false)); - setCorrelationData((store) => setCorrelationId(store, '')); - setCorrelationData((store) => setActiveCorrelation(store, null)); - setIsCorrelationEnabled(false); - setAppStore(syncTimeRange); - }} - /> - }> - Join Applied - - )} -
-
- - - -
-
-
-
-
- {/* */} -
-
- - - - -
-
- - - -
-
-
-
- - - + {Object.keys(selectedFields).length > 0 && ( <> {viewMode === 'table' ? ( diff --git a/src/pages/Correlation/providers/CorrelationProvider.tsx b/src/pages/Correlation/providers/CorrelationProvider.tsx index f7d1768d..a2498ba8 100644 --- a/src/pages/Correlation/providers/CorrelationProvider.tsx +++ b/src/pages/Correlation/providers/CorrelationProvider.tsx @@ -433,10 +433,12 @@ const setSelectedFields = ( ...store.tableOpts, pageData: updatedPageData || [], currentPage, - totalPages: getTotalPages( - filterAndSortData(store.tableOpts, store.streamData[streamName]?.logData || []), - store.tableOpts.perPage, - ), + totalPages: store.isCorrelatedData + ? store.tableOpts.totalPages + : getTotalPages( + filterAndSortData(store.tableOpts, store.streamData[streamName]?.logData || []), + store.tableOpts.perPage, + ), }, }; }; diff --git a/src/pages/Correlation/styles/Correlation.module.css b/src/pages/Correlation/styles/Correlation.module.css index 9b58053f..83af18cc 100644 --- a/src/pages/Correlation/styles/Correlation.module.css +++ b/src/pages/Correlation/styles/Correlation.module.css @@ -262,3 +262,11 @@ text-transform: uppercase; font-weight: 700; } + +.saveCorrelationIcon { + background-color: white; + border: 1px solid #e0e0e0; + &:hover { + background-color: #e0e0e0; + } +} From 84e4b71137ff49358df93af422c73e79e779470f Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Thu, 23 Jan 2025 11:01:21 +0530 Subject: [PATCH 12/23] JSON View fixes --- src/pages/Correlation/Views/CorrelationJSONView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/Correlation/Views/CorrelationJSONView.tsx b/src/pages/Correlation/Views/CorrelationJSONView.tsx index 164ca7a5..12d38f23 100644 --- a/src/pages/Correlation/Views/CorrelationJSONView.tsx +++ b/src/pages/Correlation/Views/CorrelationJSONView.tsx @@ -314,7 +314,7 @@ const CorrleationJSONView = (props: { errorMessage: string | null; hasNoData: bo return ( - + {/* */} {!errorMessage ? ( showTable ? ( @@ -322,7 +322,7 @@ const CorrleationJSONView = (props: { errorMessage: string | null; hasNoData: bo className={classes.innerContainer} style={{ display: 'flex', flexDirection: 'row', maxHeight: `calc(100vh - ${primaryHeaderHeight}px )` }}> - + From 7f24163bede247a12be925bc55193997c5b0a34e Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Sun, 26 Jan 2025 16:43:23 +0530 Subject: [PATCH 13/23] Build fix --- .../Correlation/Views/CorrelationJSONView.tsx | 124 ++---------------- 1 file changed, 11 insertions(+), 113 deletions(-) diff --git a/src/pages/Correlation/Views/CorrelationJSONView.tsx b/src/pages/Correlation/Views/CorrelationJSONView.tsx index 12d38f23..afcd7778 100644 --- a/src/pages/Correlation/Views/CorrelationJSONView.tsx +++ b/src/pages/Correlation/Views/CorrelationJSONView.tsx @@ -1,23 +1,22 @@ -import { Box, Button, Loader, Stack, TextInput, Text } from '@mantine/core'; -import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; -import classes from '../../Stream/styles/JSONView.module.css'; -import EmptyBox from '@/components/Empty'; -import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; +import { Box, Stack } from '@mantine/core'; +import { ErrorView, LoadingView } from '@/pages/Stream/Views/Explore/LoadingViews'; +import { IconCheck, IconCopy, IconDotsVertical } from '@tabler/icons-react'; import { PRIMARY_HEADER_HEIGHT, STREAM_PRIMARY_TOOLBAR_CONTAINER_HEIGHT, STREAM_SECONDARY_TOOLBAR_HRIGHT, } from '@/constants/theme'; +import { ReactNode, useCallback, useEffect, useRef, useState } from 'react'; +import { formatLogTs, isJqSearch } from '@/pages/Stream/providers/LogsProvider'; + +import EmptyBox from '@/components/Empty'; import { Log } from '@/@types/parseable/api/query'; import _ from 'lodash'; -import { IconCheck, IconCopy, IconDotsVertical, IconSearch } from '@tabler/icons-react'; +import classes from '../../Stream/styles/JSONView.module.css'; import { copyTextToClipboard } from '@/utils'; import timeRangeUtils from '@/utils/timeRangeUtils'; -import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; -import { ErrorView, LoadingView } from '@/pages/Stream/Views/Explore/LoadingViews'; -import { formatLogTs, isJqSearch } from '@/pages/Stream/providers/LogsProvider'; -import jqSearch from '@/utils/jqSearch'; -import { useHotkeys } from '@mantine/hooks'; +import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; +import { useCorrelationStore } from '../providers/CorrelationProvider'; type ContextMenuState = { visible: boolean; @@ -26,8 +25,6 @@ type ContextMenuState = { row: Log | null; }; -const { setInstantSearchValue, applyInstantSearch, applyJqSearch } = correlationStoreReducers; - const Item = (props: { header: string | null; value: string; highlight: boolean }) => { return ( @@ -173,105 +170,6 @@ const JsonRows = (props: { isSearching: boolean; setContextMenu: any }) => { ); }; -const Toolbar = ({ - isSearching, - setSearching, -}: { - isSearching: boolean; - setSearching: React.Dispatch>; -}) => { - const [localSearchValue, setLocalSearchValue] = useState(''); - const searchInputRef = useRef(null); - - const [searchValue, setCorrelationData] = useCorrelationStore((store) => store.tableOpts.instantSearchValue); - const [pageData] = useCorrelationStore((store) => store.tableOpts); - const debouncedSearch = useCallback( - _.debounce(async (val: string) => { - if (val.trim() === '') { - setCorrelationData((store) => setInstantSearchValue(store, '')); - setCorrelationData(applyInstantSearch); - } else { - const isJq = isJqSearch(val); - if (isJq) { - const jqResult = await jqSearch(pageData, val); - setCorrelationData((store) => applyJqSearch(store, jqResult)); - } else { - setCorrelationData(applyInstantSearch); - } - } - setSearching(false); - }, 500), - [pageData], - ); - - const handleSearch = useCallback(() => { - if (localSearchValue.trim()) { - setSearching(true); - setCorrelationData((store) => setInstantSearchValue(store, localSearchValue)); - debouncedSearch(localSearchValue); - } - }, [localSearchValue, debouncedSearch, setSearching]); - - const handleInputChange = useCallback( - (e: React.ChangeEvent) => { - const value = e.target.value; - setLocalSearchValue(value); - if (value.trim() === '') { - debouncedSearch(value); - } - }, - [debouncedSearch], - ); - - useHotkeys([['mod+K', () => searchInputRef.current?.focus()]]); - - const handleKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (e.key === 'Enter' && !isSearching && localSearchValue.trim()) { - handleSearch(); - } - }, - [isSearching, localSearchValue], - ); - - if (_.isEmpty(pageData)) return null; - - const inputStyles = { - '--input-left-section-width': '2rem', - '--input-right-section-width': '6rem', - width: '100%', - } as React.CSSProperties; - - return ( -
- : } - placeholder="Search loaded data with text or jq. For jq input try `jq .[]`" - value={localSearchValue} - onChange={handleInputChange} - onKeyDown={handleKeyDown} - ref={searchInputRef} - classNames={{ input: classes.inputField }} - style={inputStyles} - rightSection={ - searchValue && !isSearching ? ( - - Matches - - ) : null - } - /> - -
- ); -}; - const TableContainer = (props: { children: ReactNode }) => { return {props.children}; }; @@ -287,7 +185,7 @@ const CorrleationJSONView = (props: { errorMessage: string | null; hasNoData: bo const contextMenuRef = useRef(null); const { errorMessage, hasNoData, showTable } = props; - const [isSearching, setSearching] = useState(false); + const [isSearching] = useState(false); const primaryHeaderHeight = !maximized ? PRIMARY_HEADER_HEIGHT + STREAM_PRIMARY_TOOLBAR_CONTAINER_HEIGHT + STREAM_SECONDARY_TOOLBAR_HRIGHT : 0; From 5741818b5204c8828c0021e46e9554e663a8b632 Mon Sep 17 00:00:00 2001 From: Praveen K B <30530587+praveen5959@users.noreply.github.com> Date: Thu, 23 Jan 2025 17:44:01 +0530 Subject: [PATCH 14/23] fix: Removed /info call dependancy from explore page (#431) --- .../Stream/Views/Explore/useLogsFetcher.ts | 11 ++-- src/pages/Stream/Views/Manage/Info.tsx | 52 ++++++++++++------- src/pages/Stream/Views/Manage/Management.tsx | 4 +- .../Stream/components/EventTimeLineGraph.tsx | 9 +--- src/pages/Stream/index.tsx | 11 +--- 5 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/pages/Stream/Views/Explore/useLogsFetcher.ts b/src/pages/Stream/Views/Explore/useLogsFetcher.ts index 58433b6a..30fa0995 100644 --- a/src/pages/Stream/Views/Explore/useLogsFetcher.ts +++ b/src/pages/Stream/Views/Explore/useLogsFetcher.ts @@ -3,7 +3,6 @@ import { useEffect } from 'react'; import { useLogsStore, logsStoreReducers } from '../../providers/LogsProvider'; import { useQueryLogs } from '@/hooks/useQueryLogs'; import { useFetchCount } from '@/hooks/useQueryResult'; -import { useStreamStore } from '../../providers/StreamProvider'; import useParamsController from '@/pages/Stream/hooks/useParamsController'; import _ from 'lodash'; @@ -18,8 +17,6 @@ const useLogsFetcher = (props: { schemaLoading: boolean; infoLoading: boolean }) const [{ tableOpts }, setLogsStore] = useLogsStore((store) => store); const { currentOffset, currentPage, pageData } = tableOpts; const { getQueryData, loading: logsLoading, queryLogsError } = useQueryLogs(); - const [{ info }] = useStreamStore((store) => store); - const firstEventAt = 'first-event-at' in info ? info['first-event-at'] : undefined; const { refetchCount, countLoading } = useFetchCount(); const hasContentLoaded = schemaLoading === false && logsLoading === false; @@ -33,21 +30,21 @@ const useLogsFetcher = (props: { schemaLoading: boolean; infoLoading: boolean }) }, [currentStream]); useEffect(() => { - if (infoLoading || !firstEventAt) return; + if (infoLoading) return; if (currentPage === 0) { getQueryData(); refetchCount(); } - }, [currentPage, currentStream, timeRange, infoLoading, firstEventAt]); + }, [currentPage, currentStream, timeRange, infoLoading]); useEffect(() => { - if (infoLoading || !firstEventAt) return; + if (infoLoading) return; if (currentOffset !== 0 && currentPage !== 0) { getQueryData(); } - }, [currentOffset, infoLoading, firstEventAt]); + }, [currentOffset, infoLoading]); return { logsLoading: infoLoading || logsLoading, diff --git a/src/pages/Stream/Views/Manage/Info.tsx b/src/pages/Stream/Views/Manage/Info.tsx index d04aaf5a..64d20f08 100644 --- a/src/pages/Stream/Views/Manage/Info.tsx +++ b/src/pages/Stream/Views/Manage/Info.tsx @@ -1,4 +1,4 @@ -import { Group, Stack, Text } from '@mantine/core'; +import { Group, Loader, Stack, Text } from '@mantine/core'; import classes from '../../styles/Management.module.css'; import { useStreamStore } from '../../providers/StreamProvider'; import _ from 'lodash'; @@ -6,6 +6,7 @@ import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; import UpdateTimePartitionLimit from './UpdateTimePartitionLimit'; import UpdateCustomPartitionField from './UpdateCustomPartitionField'; import timeRangeUtils from '@/utils/timeRangeUtils'; +import ErrorView from './ErrorView'; const { formatDateWithTimezone } = timeRangeUtils; @@ -42,7 +43,7 @@ const InfoItem = (props: { title: string; value: string; fullWidth?: boolean }) ); }; -const InfoData = () => { +const InfoData = (props: { isLoading: boolean }) => { const [info] = useStreamStore((store) => store.info); const [currentStream] = useAppStore((store) => store.currentStream); @@ -59,33 +60,44 @@ const InfoData = () => { return ( - - - - - + {props.isLoading ? ( + + + + - - - - + ) : ( + + + + + + + + + + + + + + - - - - + )} ); }; -const Info = () => { +const Info = (props: { isLoading: boolean; isError: boolean }) => { return (
- + {props.isError ? : } ); }; diff --git a/src/pages/Stream/Views/Manage/Management.tsx b/src/pages/Stream/Views/Manage/Management.tsx index 7ac8991b..9c993a9e 100644 --- a/src/pages/Stream/Views/Manage/Management.tsx +++ b/src/pages/Stream/Views/Manage/Management.tsx @@ -7,6 +7,7 @@ import Stats from './Stats'; import { useLogStreamStats } from '@/hooks/useLogStreamStats'; import Info from './Info'; import DeleteStreamModal from '../../components/DeleteStreamModal'; +import { useGetStreamInfo } from '@/hooks/useGetStreamInfo'; import { useRetentionQuery } from '@/hooks/useRetentionEditor'; import { useHotTier } from '@/hooks/useHotTier'; @@ -19,6 +20,7 @@ const Management = (props: { schemaLoading: boolean }) => { const getStreamAlertsConfig = useAlertsQuery(currentStream || '', hasAlertsAccess, isStandAloneMode); const getStreamStats = useLogStreamStats(currentStream || ''); const getRetentionConfig = useRetentionQuery(currentStream || '', hasSettingsAccess); + const getStreamInfo = useGetStreamInfo(currentStream || '', currentStream !== null); const hotTierFetch = useHotTier(currentStream || '', hasSettingsAccess); // todo - handle loading and error states separately @@ -34,7 +36,7 @@ const Management = (props: { schemaLoading: boolean }) => { isLoading={getStreamStats.getLogStreamStatsDataIsLoading} isError={getStreamStats.getLogStreamStatsDataIsError} /> - + diff --git a/src/pages/Stream/components/EventTimeLineGraph.tsx b/src/pages/Stream/components/EventTimeLineGraph.tsx index d5f778ad..b988c770 100644 --- a/src/pages/Stream/components/EventTimeLineGraph.tsx +++ b/src/pages/Stream/components/EventTimeLineGraph.tsx @@ -10,7 +10,6 @@ import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/Ap import { LogsResponseWithHeaders } from '@/@types/parseable/api/query'; import _ from 'lodash'; import timeRangeUtils from '@/utils/timeRangeUtils'; -import { useStreamStore } from '../providers/StreamProvider'; const { setTimeRange } = appStoreReducers; const { makeTimeRangeLabel } = timeRangeUtils; @@ -163,15 +162,13 @@ const EventTimeLineGraph = () => { const [{ custSearchQuery }, setLogStore] = useLogsStore((store) => store.custQuerySearchState); const [{ interval, startTime, endTime }] = useAppStore((store) => store.timeRange); const [localStream, setLocalStream] = useState(''); - const [{ info }] = useStreamStore((store) => store); - const firstEventAt = 'first-event-at' in info ? info['first-event-at'] : undefined; useEffect(() => { setLocalStream(currentStream); }, [currentStream]); useEffect(() => { - if (!localStream || localStream.length === 0 || !firstEventAt) return; + if (!localStream || localStream.length === 0) return; const totalMinutes = interval / (1000 * 60); const numBins = Math.trunc(totalMinutes < 10 ? totalMinutes : totalMinutes < 60 ? 10 : 60); @@ -189,15 +186,13 @@ const EventTimeLineGraph = () => { dayjs(startTime).startOf('minute').toISOString(), dayjs(endTime).startOf('minute').toISOString(), custSearchQuery, - firstEventAt, ]); const isLoading = fetchGraphDataMutation.isLoading; const avgEventCount = useMemo(() => calcAverage(fetchGraphDataMutation?.data), [fetchGraphDataMutation?.data]); const graphData = useMemo(() => { - if (!firstEventAt) return null; return parseGraphData(fetchGraphDataMutation?.data, avgEventCount, interval); - }, [fetchGraphDataMutation?.data, interval, firstEventAt]); + }, [fetchGraphDataMutation?.data, interval]); const hasData = Array.isArray(graphData) && graphData.length !== 0; const [, setAppStore] = useAppStore((_store) => null); const setTimeRangeFromGraph = useCallback((barValue: any) => { diff --git a/src/pages/Stream/index.tsx b/src/pages/Stream/index.tsx index 7552e3a5..8cef3218 100644 --- a/src/pages/Stream/index.tsx +++ b/src/pages/Stream/index.tsx @@ -17,7 +17,6 @@ import { Text } from '@mantine/core'; import { RetryBtn } from '@/components/Button/Retry'; import LogsView from './Views/Explore/LogsView'; import { useGetStreamSchema } from '@/hooks/useGetLogStreamSchema'; -import { useGetStreamInfo } from '@/hooks/useGetStreamInfo'; import useParamsController from './hooks/useParamsController'; const { streamChangeCleanup, toggleSideBar } = streamStoreReducers; @@ -45,10 +44,6 @@ const Stream: FC = () => { const [maximized] = useAppStore((store) => store.maximized); const [instanceConfig] = useAppStore((store) => store.instanceConfig); const [, setStreamStore] = useStreamStore((store) => store.sideBarOpen); - const { getStreamInfoRefetch, getStreamInfoLoading, getStreamInfoRefetching } = useGetStreamInfo( - currentStream || '', - currentStream !== null, - ); useHotkeys([['mod+/', () => setStreamStore((store) => toggleSideBar(store))]]); @@ -62,7 +57,6 @@ const Stream: FC = () => { const fetchSchema = useCallback(() => { setStreamStore(streamChangeCleanup); - getStreamInfoRefetch(); refetchSchema(); }, [currentStream]); @@ -71,7 +65,6 @@ const Stream: FC = () => { if (!_.isEmpty(currentStream)) { if (view === 'explore') { setStreamStore(streamChangeCleanup); - getStreamInfoRefetch(); } else { fetchSchema(); } @@ -85,9 +78,7 @@ const Stream: FC = () => { if (!_.includes(STREAM_VIEWS, view)) return null; const isSchemaFetching = isSchemaRefetching || isSchemaLoading; - const isInfoLoading = - (!isStoreSynced || getStreamInfoLoading || getStreamInfoRefetching || instanceConfig === null) && - view === 'explore'; + const isInfoLoading = (!isStoreSynced || instanceConfig === null) && view === 'explore'; return ( Date: Thu, 23 Jan 2025 17:44:15 +0530 Subject: [PATCH 15/23] fix: Filters dropdown text fix in case of undefined (#432) --- src/pages/Stream/components/Querier/FilterQueryBuilder.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Stream/components/Querier/FilterQueryBuilder.tsx b/src/pages/Stream/components/Querier/FilterQueryBuilder.tsx index ba918e08..a55dbf5e 100644 --- a/src/pages/Stream/components/Querier/FilterQueryBuilder.tsx +++ b/src/pages/Stream/components/Querier/FilterQueryBuilder.tsx @@ -69,7 +69,7 @@ const RuleView = (props: RuleViewType) => { const getUniqueColValues = useMemo(() => { if (!rule.field) return []; return Array.from( - new Set(pageData.filter((item) => item[rule.field] !== null).map((item) => String(item[rule.field]))), + new Set(pageData.filter((item) => item[rule.field] != null).map((item) => String(item[rule.field]))), ); }, [pageData, rule.field]); From bf2f385679fc18fe14af4de9956beafa9774d870 Mon Sep 17 00:00:00 2001 From: Koustav Das <78158736+Koustavd18@users.noreply.github.com> Date: Thu, 23 Jan 2025 17:44:26 +0530 Subject: [PATCH 16/23] Esc key press to close querier modal (#434) --- src/pages/Stream/components/Querier/index.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/pages/Stream/components/Querier/index.tsx b/src/pages/Stream/components/Querier/index.tsx index f0b39565..5a4d001a 100644 --- a/src/pages/Stream/components/Querier/index.tsx +++ b/src/pages/Stream/components/Querier/index.tsx @@ -107,6 +107,20 @@ const QuerierModal = (props: { } }, [showQueryBuilder]); + useEffect(() => { + const handleKeyPress = (event: { key: string }) => { + if (event.key === 'Escape') { + onClose(); + } + }; + + window.addEventListener('keydown', handleKeyPress); + + return () => { + window.removeEventListener('keydown', handleKeyPress); + }; + }, []); + return ( Date: Thu, 23 Jan 2025 17:44:45 +0530 Subject: [PATCH 17/23] fix: hot tier refresh (#429) --- src/hooks/useHotTier.ts | 1 + src/pages/Stream/Views/Manage/Management.tsx | 1 + src/pages/Stream/Views/Manage/Settings.tsx | 57 +++++++++++++++----- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/src/hooks/useHotTier.ts b/src/hooks/useHotTier.ts index ecd928e1..a38e6204 100644 --- a/src/hooks/useHotTier.ts +++ b/src/hooks/useHotTier.ts @@ -63,6 +63,7 @@ export const useHotTier = (streamName: string, hasSettingsAccess: boolean) => { return { getHotTierInfoError, getHotTierInfoLoading, + refetchHotTierInfo, updateHotTier, deleteHotTier, isDeleting, diff --git a/src/pages/Stream/Views/Manage/Management.tsx b/src/pages/Stream/Views/Manage/Management.tsx index 9c993a9e..42b7d871 100644 --- a/src/pages/Stream/Views/Manage/Management.tsx +++ b/src/pages/Stream/Views/Manage/Management.tsx @@ -44,6 +44,7 @@ const Management = (props: { schemaLoading: boolean }) => { isLoading={isHotTierLoading || isRetentionLoading} updateRetentionConfig={getRetentionConfig.updateLogStreamRetention} updateHotTierInfo={hotTierFetch.updateHotTier} + refetchHotTierInfo={hotTierFetch.refetchHotTierInfo} deleteHotTierInfo={hotTierFetch.deleteHotTier} isDeleting={hotTierFetch.isDeleting} isUpdating={hotTierFetch.isUpdating} diff --git a/src/pages/Stream/Views/Manage/Settings.tsx b/src/pages/Stream/Views/Manage/Settings.tsx index d291851e..f53ba9d0 100644 --- a/src/pages/Stream/Views/Manage/Settings.tsx +++ b/src/pages/Stream/Views/Manage/Settings.tsx @@ -1,4 +1,4 @@ -import { Box, Button, Divider, Loader, Modal, NumberInput, Stack, TextInput } from '@mantine/core'; +import { Box, Button, Divider, Group, Loader, Modal, NumberInput, px, Stack, TextInput } from '@mantine/core'; import classes from '../../styles/Management.module.css'; import { Text } from '@mantine/core'; import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; @@ -6,14 +6,17 @@ import { useForm } from '@mantine/form'; import _ from 'lodash'; import { useCallback, useEffect, useState } from 'react'; import { useStreamStore } from '../../providers/StreamProvider'; -import { IconCheck, IconTrash, IconX } from '@tabler/icons-react'; +import { IconCheck, IconX, IconReload } from '@tabler/icons-react'; import { sanitizeBytes, convertGibToBytes } from '@/utils/formatBytes'; import timeRangeUtils from '@/utils/timeRangeUtils'; import ErrorView from './ErrorView'; import RestrictedView from '@/components/Misc/RestrictedView'; +import IconButton from '@/components/Button/IconButton'; const { formatDateWithTimezone } = timeRangeUtils; +const renderRefreshIcon = () => ; + const Header = () => { return ( @@ -168,6 +171,7 @@ const DeleteHotTierModal = (props: { const HotTierConfig = (props: { updateHotTierInfo: ({ size }: { size: string }) => void; + refetchHotTierInfo: () => void; deleteHotTierInfo: ({ onSuccess }: { onSuccess: () => void }) => void; isDeleting: boolean; isUpdating: boolean; @@ -228,21 +232,23 @@ const HotTierConfig = (props: { /> Hot Tier Storage Size - {!hotTierNotSet && streamType === 'UserDefined' ? ( - + {!hotTierNotSet ? ( + + + ) : null} - - - Oldest Record: - - {_.isEmpty(oldestEntry) ? 'No Entries Stored' : formatDateWithTimezone(oldestEntry)} - - + - + {streamType === 'UserDefined' ? ( {humanizedUsedSize} used | {humanizedAvailableSize} available @@ -265,7 +271,7 @@ const HotTierConfig = (props: { @@ -289,6 +295,18 @@ const HotTierConfig = (props: { {props.isUpdating && } + + {!hotTierNotSet && streamType === 'UserDefined' ? ( + + + + + + ) : null} + From 497d32b3f4603736663c8a306bc97b7efcef6564 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Mon, 27 Jan 2025 14:54:46 +0530 Subject: [PATCH 20/23] fix: Fixed the navigate from explore page --- .../components/SavedCorrelationItem.tsx | 14 ++++++++------ src/pages/Correlation/index.tsx | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/pages/Correlation/components/SavedCorrelationItem.tsx b/src/pages/Correlation/components/SavedCorrelationItem.tsx index 318ffd48..e785dabd 100644 --- a/src/pages/Correlation/components/SavedCorrelationItem.tsx +++ b/src/pages/Correlation/components/SavedCorrelationItem.tsx @@ -1,14 +1,15 @@ -import { Stack, Box, Button, Text, px, Code } from '@mantine/core'; +import { Box, Button, Code, Stack, Text, px } from '@mantine/core'; +import { FC, useCallback, useState } from 'react'; import { IconClock, IconEye, IconEyeOff, IconTrash, IconX } from '@tabler/icons-react'; -import { useState, useCallback, FC } from 'react'; -import classes from '../styles/SavedCorrelationItem.module.css'; +import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; + import { Correlation } from '@/@types/parseable/api/correlation'; -import dayjs from 'dayjs'; import IconButton from '@/components/Button/IconButton'; +import classes from '../styles/SavedCorrelationItem.module.css'; +import dayjs from 'dayjs'; import { useCorrelationsQuery } from '@/hooks/useCorrelations'; -import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; -const { toggleSavedCorrelationsModal, setCorrelationId } = correlationStoreReducers; +const { toggleSavedCorrelationsModal, setCorrelationId, cleanCorrelationStore } = correlationStoreReducers; const renderDeleteIcon = () => ; const renderCloseIcon = () => ; @@ -105,6 +106,7 @@ const SavedCorrelationItem = (props: { item: Correlation }) => { }, []); const onCorrelationAppy = useCallback(() => { + setCorrelationData(cleanCorrelationStore); setCorrelationData((store) => setCorrelationId(store, id)); closeModal(); }, []); diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index 0d12e965..bd56fda7 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -196,7 +196,7 @@ const Correlation = () => { } }, [loadingState, currentPage]); - if (isLoading || !Object.keys(fields)) return; + if (isLoading || !Object.keys(fields) || !Object.keys(selectedFields)) return; return ( From 17a3bb10a78d3dc59af3b1fd0ba83813984c21a5 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Mon, 27 Jan 2025 22:06:42 +0530 Subject: [PATCH 21/23] fix: Saved correlation apply bug fix --- .../components/CorrelationSideBar.tsx | 19 ++++++++++--------- .../components/SavedCorrelationItem.tsx | 3 +-- src/pages/Correlation/index.tsx | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pages/Correlation/components/CorrelationSideBar.tsx b/src/pages/Correlation/components/CorrelationSideBar.tsx index 4955a8cb..10edfeb0 100644 --- a/src/pages/Correlation/components/CorrelationSideBar.tsx +++ b/src/pages/Correlation/components/CorrelationSideBar.tsx @@ -1,13 +1,14 @@ -import { TextInput, Text } from '@mantine/core'; -import { IconTrashX } from '@tabler/icons-react'; -import classes from '../styles/Correlation.module.css'; -import { CorrelationFieldItem } from './CorrelationFieldItem'; -import { StreamSelectBox } from './StreamSelectBox'; -import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; import { FC, useState } from 'react'; +import { Text, TextInput } from '@mantine/core'; import { appStoreReducers, useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; +import { correlationStoreReducers, useCorrelationStore } from '../providers/CorrelationProvider'; + +import { CorrelationFieldItem } from './CorrelationFieldItem'; +import { IconTrashX } from '@tabler/icons-react'; +import { StreamSelectBox } from './StreamSelectBox'; +import classes from '../styles/Correlation.module.css'; -const { changeStream } = appStoreReducers; +const { setStreamForCorrelation } = appStoreReducers; const { setIsCorrelatedFlag, setSelectedFields, deleteStreamData } = correlationStoreReducers; interface CorrelationSideBarProps { @@ -50,7 +51,7 @@ export const CorrelationSidebar: FC = ({ }; const addStream = (value: string | null) => { if (value) { - setAppStore((store) => changeStream(store, value)); + setAppStore((store) => setStreamForCorrelation(store, value)); } }; @@ -94,7 +95,7 @@ export const CorrelationSidebar: FC = ({ cursor="pointer" size={14} onClick={() => { - setAppStore((store) => changeStream(store, '')); + setAppStore((store) => setStreamForCorrelation(store, '')); setCorrelationData((store) => setIsCorrelatedFlag(store, false)); setSelect1Value({ value: null, dataType: '' }); setSelect2Value({ value: null, dataType: '' }); diff --git a/src/pages/Correlation/components/SavedCorrelationItem.tsx b/src/pages/Correlation/components/SavedCorrelationItem.tsx index e785dabd..ac43465a 100644 --- a/src/pages/Correlation/components/SavedCorrelationItem.tsx +++ b/src/pages/Correlation/components/SavedCorrelationItem.tsx @@ -9,7 +9,7 @@ import classes from '../styles/SavedCorrelationItem.module.css'; import dayjs from 'dayjs'; import { useCorrelationsQuery } from '@/hooks/useCorrelations'; -const { toggleSavedCorrelationsModal, setCorrelationId, cleanCorrelationStore } = correlationStoreReducers; +const { toggleSavedCorrelationsModal, setCorrelationId } = correlationStoreReducers; const renderDeleteIcon = () => ; const renderCloseIcon = () => ; @@ -106,7 +106,6 @@ const SavedCorrelationItem = (props: { item: Correlation }) => { }, []); const onCorrelationAppy = useCallback(() => { - setCorrelationData(cleanCorrelationStore); setCorrelationData((store) => setCorrelationId(store, id)); closeModal(); }, []); diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index bd56fda7..eaa3683d 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -27,7 +27,7 @@ import { useDocumentTitle } from '@mantine/hooks'; import { useFetchStreamData } from '@/hooks/useFetchStreamData'; import useParamsController from './hooks/useParamsController'; -const { changeStream, setTimeRange } = appStoreReducers; +const { setStreamForCorrelation, setTimeRange } = appStoreReducers; const { setSelectedFields, setCorrelationCondition, setActiveCorrelation, setPageAndPageData, setTargetPage } = correlationStoreReducers; @@ -173,7 +173,7 @@ const Correlation = () => { const updateCorrelationCondition = () => { if (select1Value.value && select2Value.value) { const condition = `"${streamNames[0]}".${select1Value.value} = "${streamNames[1]}".${select2Value.value}`; - setAppStore((store) => changeStream(store, 'correlatedStream')); + setAppStore((store) => setStreamForCorrelation(store, 'correlatedStream')); setCorrelationData((store) => setCorrelationCondition(store, condition)); } }; From 2ef9c0ddbaae03a539ab696e2eb692f975a93433 Mon Sep 17 00:00:00 2001 From: Praveen K B Date: Tue, 28 Jan 2025 12:44:30 +0530 Subject: [PATCH 22/23] fix: Clearing feilds on apply --- src/hooks/useCorrelationQueryLogs.tsx | 16 ++++++++-------- .../components/SavedCorrelationItem.tsx | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/hooks/useCorrelationQueryLogs.tsx b/src/hooks/useCorrelationQueryLogs.tsx index 998fb45a..c42ad6b2 100644 --- a/src/hooks/useCorrelationQueryLogs.tsx +++ b/src/hooks/useCorrelationQueryLogs.tsx @@ -1,18 +1,19 @@ -import { getCorrelationQueryLogsWithHeaders } from '@/api/query'; -import useMountedState from './useMountedState'; -import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; -import _ from 'lodash'; -import { AxiosError } from 'axios'; -import { useStreamStore } from '@/pages/Stream/providers/StreamProvider'; import { CORRELATION_LOAD_LIMIT, correlationStoreReducers, useCorrelationStore, } from '@/pages/Correlation/providers/CorrelationProvider'; + +import { AxiosError } from 'axios'; +import { LogsResponseWithHeaders } from '@/@types/parseable/api/query'; +import _ from 'lodash'; +import { getCorrelationQueryLogsWithHeaders } from '@/api/query'; import { notifyError } from '@/utils/notification'; +import { useAppStore } from '@/layouts/MainLayout/providers/AppProvider'; +import useMountedState from './useMountedState'; import { useQuery } from 'react-query'; -import { LogsResponseWithHeaders } from '@/@types/parseable/api/query'; import { useState } from 'react'; +import { useStreamStore } from '@/pages/Stream/providers/StreamProvider'; const { setStreamData, setIsCorrelatedFlag } = correlationStoreReducers; @@ -69,7 +70,6 @@ export const useCorrelationQueryLogs = () => { notifyError({ message: `${currentStream} doesn't have any fields` }); } }); - setCorrelationStore((store) => setIsCorrelatedFlag(store, true)); }, onError: (data: AxiosError) => { setLoading(false); diff --git a/src/pages/Correlation/components/SavedCorrelationItem.tsx b/src/pages/Correlation/components/SavedCorrelationItem.tsx index ac43465a..e785dabd 100644 --- a/src/pages/Correlation/components/SavedCorrelationItem.tsx +++ b/src/pages/Correlation/components/SavedCorrelationItem.tsx @@ -9,7 +9,7 @@ import classes from '../styles/SavedCorrelationItem.module.css'; import dayjs from 'dayjs'; import { useCorrelationsQuery } from '@/hooks/useCorrelations'; -const { toggleSavedCorrelationsModal, setCorrelationId } = correlationStoreReducers; +const { toggleSavedCorrelationsModal, setCorrelationId, cleanCorrelationStore } = correlationStoreReducers; const renderDeleteIcon = () => ; const renderCloseIcon = () => ; @@ -106,6 +106,7 @@ const SavedCorrelationItem = (props: { item: Correlation }) => { }, []); const onCorrelationAppy = useCallback(() => { + setCorrelationData(cleanCorrelationStore); setCorrelationData((store) => setCorrelationId(store, id)); closeModal(); }, []); From 9fafe70f42b155ef9fc612ab04c4c4d4a9182a84 Mon Sep 17 00:00:00 2001 From: Koustav Date: Thu, 30 Jan 2025 15:47:11 +0530 Subject: [PATCH 23/23] schema fetch logic --- src/pages/Correlation/index.tsx | 39 +++++++++++++++---- .../Stream/components/PrimaryToolbar.tsx | 23 +---------- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src/pages/Correlation/index.tsx b/src/pages/Correlation/index.tsx index eaa3683d..20879487 100644 --- a/src/pages/Correlation/index.tsx +++ b/src/pages/Correlation/index.tsx @@ -28,8 +28,15 @@ import { useFetchStreamData } from '@/hooks/useFetchStreamData'; import useParamsController from './hooks/useParamsController'; const { setStreamForCorrelation, setTimeRange } = appStoreReducers; -const { setSelectedFields, setCorrelationCondition, setActiveCorrelation, setPageAndPageData, setTargetPage } = - correlationStoreReducers; +import { getLogStreamSchema } from '@/api/logStream'; +const { + setSelectedFields, + setCorrelationCondition, + setActiveCorrelation, + setPageAndPageData, + setTargetPage, + setStreamSchema, +} = correlationStoreReducers; const Correlation = () => { useDocumentTitle('Parseable | Correlation'); @@ -62,6 +69,7 @@ const Correlation = () => { (isSavedCorrelation && activeCorrelation?.tableConfigs.map((config: { tableName: string }) => config.tableName)) || []; const { isLoading: multipleSchemasLoading } = useGetMultipleStreamSchemas(streamsToFetch); + const [schemaLoad, setSchemaLoad] = useState(false); const { getCorrelationData, loadingState, error: errorMessage } = useCorrelationQueryLogs(); const { getFetchStreamData, loading: streamsLoading } = useFetchStreamData(); @@ -140,6 +148,22 @@ const Correlation = () => { type: 'custom', }), ); + if (!activeCorrelation) return; + + const fetchSchema = async (streamName: string) => { + setSchemaLoad(true); + try { + const schema = await getLogStreamSchema(streamName); + setCorrelationData((store) => setStreamSchema(store, schema.data, streamName)); + } catch (error) { + console.log(error); + } finally { + setSchemaLoad(false); + } + }; + const streamNames = activeCorrelation.tableConfigs.map((config: { tableName: string }) => config.tableName); + streamNames.forEach(async (el) => await fetchSchema(el)); + setSelect1Value({ value: null, dataType: '' }); setSelect2Value({ value: null, dataType: '' }); setCorrelationData((store) => setCorrelationCondition(store, '')); @@ -154,21 +178,22 @@ const Correlation = () => { }, [streamForCorrelation, fields]); useEffect(() => { - if (isCorrelatedData) { + if (isCorrelatedData && !schemaLoad) { getCorrelationData(); } else { getFetchStreamData(); } - }, [currentOffset, timeRange]); + }, [currentOffset, timeRange, schemaLoad]); useEffect(() => { + if (schemaLoad) return; updateCorrelationCondition(); if (activeCorrelation && correlationCondition && isSavedCorrelation) { refetchCount(); getCorrelationData(); } correlationCondition && setIsCorrelationEnabled(true); - }, [select1Value, select2Value, activeCorrelation, correlationCondition]); + }, [select1Value, select2Value, activeCorrelation, correlationCondition, schemaLoad]); const updateCorrelationCondition = () => { if (select1Value.value && select2Value.value) { @@ -196,14 +221,14 @@ const Correlation = () => { } }, [loadingState, currentPage]); - if (isLoading || !Object.keys(fields) || !Object.keys(selectedFields)) return; + if (isLoading || schemaLoad || !Object.keys(fields) || !Object.keys(selectedFields)) return; return ( ; const renderDeleteIcon = () => ; @@ -46,24 +44,6 @@ const SavedFiltersButton = () => { ); }; -const AddCorrelationButton = () => { - const navigate = useNavigate(); - const [, setAppStore] = useAppStore(() => null); - - return ( - - ); -}; - const DeleteStreamButton = () => { const [, setLogsStore] = useLogsStore(() => null); const onClick = useCallback(() => setLogsStore(toggleDeleteModal), []); @@ -122,7 +102,6 @@ const PrimaryToolbar = () => { {view === 'explore' ? ( -