mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 06:29:02 +08:00
feat: global time is updated (#2013)
This commit is contained in:
parent
48659a2957
commit
17f32e9765
@ -2,7 +2,9 @@ import { Button, Popover } from 'antd';
|
||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
@ -14,7 +16,7 @@ function AddToQueryHOC({
|
||||
const {
|
||||
searchFilter: { queryString },
|
||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||
const dispatch = useDispatch();
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
|
||||
const generatedQuery = useMemo(
|
||||
() => generateFilterQuery({ fieldKey, fieldValue, type: 'IN' }),
|
||||
@ -31,7 +33,9 @@ function AddToQueryHOC({
|
||||
}
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_STRING,
|
||||
payload: updatedQueryString,
|
||||
payload: {
|
||||
searchQueryString: updatedQueryString,
|
||||
},
|
||||
});
|
||||
}, [dispatch, generatedQuery, queryString]);
|
||||
|
||||
|
@ -101,15 +101,9 @@ function LogItem({ logData }: LogItemProps): JSX.Element {
|
||||
{'{'}
|
||||
<LogContainer>
|
||||
<>
|
||||
<LogGeneralField
|
||||
fieldKey="log"
|
||||
fieldValue={flattenLogData.body as never}
|
||||
/>
|
||||
<LogGeneralField fieldKey="log" fieldValue={flattenLogData.body} />
|
||||
{flattenLogData.stream && (
|
||||
<LogGeneralField
|
||||
fieldKey="stream"
|
||||
fieldValue={flattenLogData.stream as never}
|
||||
/>
|
||||
<LogGeneralField fieldKey="stream" fieldValue={flattenLogData.stream} />
|
||||
)}
|
||||
<LogGeneralField
|
||||
fieldKey="timestamp"
|
||||
|
@ -37,6 +37,7 @@ const themeColors = {
|
||||
matterhornGrey: '#555555',
|
||||
whiteCream: '#ffffffd5',
|
||||
black: '#000000',
|
||||
lightgrey: '#ddd',
|
||||
};
|
||||
|
||||
export { themeColors };
|
||||
|
@ -4,6 +4,8 @@ import {
|
||||
RightOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { Button, Divider, Select } from 'antd';
|
||||
import { getGlobalTime } from 'container/LogsSearchFilter/utils';
|
||||
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
@ -13,6 +15,7 @@ import {
|
||||
RESET_ID_START_AND_END,
|
||||
SET_LOG_LINES_PER_PAGE,
|
||||
} from 'types/actions/logs';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
import { ITEMS_PER_PAGE_OPTIONS } from './config';
|
||||
@ -28,18 +31,33 @@ function LogControls(): JSX.Element | null {
|
||||
isLoadingAggregate,
|
||||
logs,
|
||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleLogLinesPerPageChange = (e: number): void => {
|
||||
dispatch({
|
||||
type: SET_LOG_LINES_PER_PAGE,
|
||||
payload: e,
|
||||
payload: {
|
||||
logLinesPerPage: e,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleGoToLatest = (): void => {
|
||||
const { maxTime, minTime } = getMinMax(
|
||||
globalTime.selectedTime,
|
||||
globalTime.minTime,
|
||||
globalTime.maxTime,
|
||||
);
|
||||
|
||||
dispatch({
|
||||
type: RESET_ID_START_AND_END,
|
||||
payload: getGlobalTime(globalTime.selectedTime, {
|
||||
maxTime,
|
||||
minTime,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -4,7 +4,7 @@ import getStep from 'lib/getStep';
|
||||
import { generateFilterQuery } from 'lib/logs/generateFilterQuery';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { getLogs } from 'store/actions/logs/getLogs';
|
||||
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
||||
@ -46,7 +46,7 @@ function ActionItem({
|
||||
liveTail,
|
||||
idEnd,
|
||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||
const dispatch = useDispatch();
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
@ -62,7 +62,9 @@ function ActionItem({
|
||||
}
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_STRING,
|
||||
payload: updatedQueryString,
|
||||
payload: {
|
||||
searchQueryString: updatedQueryString,
|
||||
},
|
||||
});
|
||||
|
||||
if (liveTail === 'STOPPED') {
|
||||
|
@ -64,7 +64,6 @@ function TableView({ logData }: TableViewProps): JSX.Element | null {
|
||||
if (!RESTRICTED_FIELDS.includes(fieldKey[0])) {
|
||||
return (
|
||||
<AddToQueryHOC fieldKey={fieldKey[0]} fieldValue={flattenLogData[field]}>
|
||||
{' '}
|
||||
{renderedField}
|
||||
</AddToQueryHOC>
|
||||
);
|
||||
|
@ -1,20 +1,17 @@
|
||||
import { LoadingOutlined } from '@ant-design/icons';
|
||||
import { Button, Popover, Spin } from 'antd';
|
||||
import { Button, Popover, Spin, Typography } from 'antd';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import {
|
||||
IField,
|
||||
IInterestingFields,
|
||||
ISelectedFields,
|
||||
} from 'types/api/logs/fields';
|
||||
|
||||
import { ICON_STYLE } from './config';
|
||||
import { Field } from './styles';
|
||||
|
||||
interface FieldItemProps {
|
||||
name: string;
|
||||
buttonIcon: React.ReactNode;
|
||||
buttonOnClick: (arg0: Record<string, unknown>) => void;
|
||||
fieldData: Record<string, never>;
|
||||
fieldIndex: number;
|
||||
isLoading: boolean;
|
||||
iconHoverText: string;
|
||||
}
|
||||
export function FieldItem({
|
||||
function FieldItem({
|
||||
name,
|
||||
buttonIcon,
|
||||
buttonOnClick,
|
||||
@ -23,33 +20,65 @@ export function FieldItem({
|
||||
isLoading,
|
||||
iconHoverText,
|
||||
}: FieldItemProps): JSX.Element {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
const [isHovered, setIsHovered] = useState<boolean>(false);
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const onClickHandler = useCallback(() => {
|
||||
if (!isLoading && buttonOnClick) buttonOnClick({ fieldData, fieldIndex });
|
||||
}, [buttonOnClick, fieldData, fieldIndex, isLoading]);
|
||||
|
||||
const renderContent = useMemo(() => {
|
||||
if (isLoading) {
|
||||
return <Spin spinning size="small" indicator={<LoadingOutlined spin />} />;
|
||||
}
|
||||
|
||||
if (isHovered) {
|
||||
return (
|
||||
<Popover content={<Typography>{iconHoverText}</Typography>}>
|
||||
<Button
|
||||
size="small"
|
||||
type="text"
|
||||
icon={buttonIcon}
|
||||
onClick={onClickHandler}
|
||||
/>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}, [buttonIcon, iconHoverText, isHovered, isLoading, onClickHandler]);
|
||||
|
||||
const onMouseHoverHandler = useCallback(
|
||||
(value: boolean) => (): void => {
|
||||
setIsHovered(value);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
return (
|
||||
<Field
|
||||
onMouseEnter={(): void => {
|
||||
setIsHovered(true);
|
||||
}}
|
||||
onMouseLeave={(): void => setIsHovered(false)}
|
||||
onMouseEnter={onMouseHoverHandler(true)}
|
||||
onMouseLeave={onMouseHoverHandler(false)}
|
||||
isDarkMode={isDarkMode}
|
||||
>
|
||||
<span>{name}</span>
|
||||
{isLoading ? (
|
||||
<Spin spinning size="small" indicator={<LoadingOutlined spin />} />
|
||||
) : (
|
||||
isHovered &&
|
||||
buttonOnClick && (
|
||||
<Popover content={<span>{iconHoverText}</span>}>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
icon={buttonIcon}
|
||||
onClick={(): void => buttonOnClick({ fieldData, fieldIndex })}
|
||||
style={{ color: 'inherit', padding: 0, height: '1rem', width: '1rem' }}
|
||||
/>
|
||||
</Popover>
|
||||
)
|
||||
)}
|
||||
<Typography style={ICON_STYLE.PLUS}>{name}</Typography>
|
||||
|
||||
{renderContent}
|
||||
</Field>
|
||||
);
|
||||
}
|
||||
|
||||
interface FieldItemProps {
|
||||
name: string;
|
||||
buttonIcon: React.ReactNode;
|
||||
buttonOnClick: (props: {
|
||||
fieldData: IInterestingFields | ISelectedFields;
|
||||
fieldIndex: number;
|
||||
}) => void;
|
||||
fieldData: IField;
|
||||
fieldIndex: number;
|
||||
isLoading: boolean;
|
||||
iconHoverText: string;
|
||||
}
|
||||
|
||||
export default FieldItem;
|
||||
|
8
frontend/src/container/LogsFilters/config.ts
Normal file
8
frontend/src/container/LogsFilters/config.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { blue, red } from '@ant-design/colors';
|
||||
|
||||
export const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
|
||||
|
||||
export const ICON_STYLE = {
|
||||
PLUS: { color: blue[5] },
|
||||
CLOSE: { color: red[5] },
|
||||
};
|
@ -1,27 +1,19 @@
|
||||
/* eslint-disable react/no-array-index-key */
|
||||
import { red } from '@ant-design/colors';
|
||||
import { CloseOutlined, PlusCircleFilled } from '@ant-design/icons';
|
||||
import { Input } from 'antd';
|
||||
import AddToSelectedFields from 'api/logs/AddToSelectedField';
|
||||
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
|
||||
import CategoryHeading from 'components/Logs/CategoryHeading';
|
||||
import { fieldSearchFilter } from 'lib/logs/fieldSearch';
|
||||
import React, { memo, useState } from 'react';
|
||||
import { connect, useSelector } from 'react-redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { GetLogsFields } from 'store/actions/logs/getFields';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import { IInterestingFields, ISelectedFields } from 'types/api/logs/fields';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
import { FieldItem } from './FieldItem';
|
||||
import { ICON_STYLE } from './config';
|
||||
import FieldItem from './FieldItem';
|
||||
import { CategoryContainer, Container, FieldContainer } from './styles';
|
||||
import { IHandleInterestProps, IHandleRemoveInterestProps } from './types';
|
||||
import { onHandleAddInterest, onHandleRemoveInterest } from './utils';
|
||||
|
||||
const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id'];
|
||||
|
||||
function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
||||
function LogsFilters(): JSX.Element {
|
||||
const {
|
||||
fields: { interesting, selected },
|
||||
} = useSelector<AppState, ILogsReducer>((state) => state.logs);
|
||||
@ -36,57 +28,40 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
||||
setFilterValuesInput((e.target as HTMLInputElement).value);
|
||||
};
|
||||
|
||||
const handleAddInterestingToSelected = async ({
|
||||
fieldData,
|
||||
fieldIndex,
|
||||
}: {
|
||||
fieldData: IInterestingFields;
|
||||
fieldIndex: number;
|
||||
}): Promise<void> => {
|
||||
setInterestingFieldLoading((prevState: number[]) => {
|
||||
prevState.push(fieldIndex);
|
||||
return [...prevState];
|
||||
});
|
||||
const onHandleAddSelectedToInteresting = useCallback(
|
||||
({ fieldData, fieldIndex }: IHandleInterestProps) => (): Promise<void> =>
|
||||
onHandleAddInterest({
|
||||
fieldData,
|
||||
fieldIndex,
|
||||
interesting,
|
||||
interestingFieldLoading,
|
||||
setInterestingFieldLoading,
|
||||
selected,
|
||||
}),
|
||||
[interesting, interestingFieldLoading, selected],
|
||||
);
|
||||
|
||||
await AddToSelectedFields({
|
||||
...fieldData,
|
||||
selected: true,
|
||||
});
|
||||
getLogsFields();
|
||||
const onHandleRemoveSelected = useCallback(
|
||||
({
|
||||
fieldData,
|
||||
fieldIndex,
|
||||
}: IHandleRemoveInterestProps) => (): Promise<void> =>
|
||||
onHandleRemoveInterest({
|
||||
fieldData,
|
||||
fieldIndex,
|
||||
interesting,
|
||||
interestingFieldLoading,
|
||||
selected,
|
||||
setSelectedFieldLoading,
|
||||
}),
|
||||
[interesting, interestingFieldLoading, selected, setSelectedFieldLoading],
|
||||
);
|
||||
|
||||
setInterestingFieldLoading(
|
||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
||||
);
|
||||
};
|
||||
const handleRemoveSelectedField = async ({
|
||||
fieldData,
|
||||
fieldIndex,
|
||||
}: {
|
||||
fieldData: ISelectedFields;
|
||||
fieldIndex: number;
|
||||
}): Promise<void> => {
|
||||
setSelectedFieldLoading((prevState) => {
|
||||
prevState.push(fieldIndex);
|
||||
return [...prevState];
|
||||
});
|
||||
|
||||
await RemoveSelectedField({
|
||||
...fieldData,
|
||||
selected: false,
|
||||
});
|
||||
|
||||
getLogsFields();
|
||||
|
||||
setSelectedFieldLoading(
|
||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Container flex="450px">
|
||||
<Input
|
||||
placeholder="Filter Values"
|
||||
onInput={handleSearch}
|
||||
style={{ width: '100%' }}
|
||||
value={filterValuesInput}
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
@ -98,15 +73,15 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
||||
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
||||
.map((field, idx) => (
|
||||
<FieldItem
|
||||
key={`${JSON.stringify(field)}-${idx}`}
|
||||
key={`${JSON.stringify(field)}`}
|
||||
name={field.name}
|
||||
fieldData={field as never}
|
||||
fieldData={field}
|
||||
fieldIndex={idx}
|
||||
buttonIcon={<CloseOutlined style={{ color: red[5] }} />}
|
||||
buttonOnClick={
|
||||
(!RESTRICTED_SELECTED_FIELDS.includes(field.name) &&
|
||||
handleRemoveSelectedField) as never
|
||||
}
|
||||
buttonIcon={<CloseOutlined style={ICON_STYLE.CLOSE} />}
|
||||
buttonOnClick={onHandleRemoveSelected({
|
||||
fieldData: field,
|
||||
fieldIndex: idx,
|
||||
})}
|
||||
isLoading={selectedFieldLoading.includes(idx)}
|
||||
iconHoverText="Remove from Selected Fields"
|
||||
/>
|
||||
@ -120,33 +95,23 @@ function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element {
|
||||
.filter((field) => fieldSearchFilter(field.name, filterValuesInput))
|
||||
.map((field, idx) => (
|
||||
<FieldItem
|
||||
key={`${JSON.stringify(field)}-${idx}`}
|
||||
key={`${JSON.stringify(field)}`}
|
||||
name={field.name}
|
||||
fieldData={field as never}
|
||||
fieldData={field}
|
||||
fieldIndex={idx}
|
||||
buttonIcon={<PlusCircleFilled />}
|
||||
buttonOnClick={handleAddInterestingToSelected as never}
|
||||
buttonIcon={<PlusCircleFilled style={ICON_STYLE.PLUS} />}
|
||||
buttonOnClick={onHandleAddSelectedToInteresting({
|
||||
fieldData: field,
|
||||
fieldIndex: idx,
|
||||
})}
|
||||
isLoading={interestingFieldLoading.includes(idx)}
|
||||
iconHoverText="Add to Selected Fields"
|
||||
/>
|
||||
))}
|
||||
</FieldContainer>
|
||||
</CategoryContainer>
|
||||
{/* <ExtractField>Extract Fields</ExtractField> */}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
getLogsFields: () => (dispatch: Dispatch<AppActions>) => void;
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (
|
||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
||||
): DispatchProps => ({
|
||||
getLogsFields: bindActionCreators(GetLogsFields, dispatch),
|
||||
});
|
||||
|
||||
type LogsFiltersProps = DispatchProps;
|
||||
|
||||
export default connect(null, mapDispatchToProps)(memo(LogsFilters));
|
||||
export default LogsFilters;
|
||||
|
@ -4,8 +4,8 @@ import styled from 'styled-components';
|
||||
|
||||
export const Container = styled(Col)`
|
||||
padding-top: 0.3rem;
|
||||
min-width: 250px;
|
||||
max-width: 350px;
|
||||
min-width: 15.625rem;
|
||||
max-width: 21.875rem;
|
||||
`;
|
||||
|
||||
export const CategoryContainer = styled.div`
|
||||
@ -21,6 +21,7 @@ export const FieldContainer = styled(Typography.Text)`
|
||||
export const Field = styled.div<{ isDarkMode: boolean }>`
|
||||
border-radius: 0.5rem;
|
||||
padding: 0.3rem 0.5rem;
|
||||
height: 2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
35
frontend/src/container/LogsFilters/types.ts
Normal file
35
frontend/src/container/LogsFilters/types.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {
|
||||
IField,
|
||||
IInterestingFields,
|
||||
ISelectedFields,
|
||||
} from 'types/api/logs/fields';
|
||||
|
||||
type SetLoading = (value: React.SetStateAction<number[]>) => void;
|
||||
|
||||
export type IHandleInterestProps = {
|
||||
fieldData: IInterestingFields;
|
||||
fieldIndex: number;
|
||||
};
|
||||
|
||||
export type IHandleRemoveInterestProps = {
|
||||
fieldData: ISelectedFields;
|
||||
fieldIndex: number;
|
||||
};
|
||||
|
||||
export interface OnHandleAddInterestProps {
|
||||
setInterestingFieldLoading: SetLoading;
|
||||
fieldIndex: number;
|
||||
fieldData: ISelectedFields;
|
||||
interesting: IField[];
|
||||
interestingFieldLoading: number[];
|
||||
selected: IField[];
|
||||
}
|
||||
|
||||
export interface OnHandleRemoveInterestProps {
|
||||
setSelectedFieldLoading: SetLoading;
|
||||
selected: IField[];
|
||||
interesting: IField[];
|
||||
interestingFieldLoading: number[];
|
||||
fieldData: IInterestingFields;
|
||||
fieldIndex: number;
|
||||
}
|
94
frontend/src/container/LogsFilters/utils.ts
Normal file
94
frontend/src/container/LogsFilters/utils.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import AddToSelectedFields from 'api/logs/AddToSelectedField';
|
||||
import RemoveSelectedField from 'api/logs/RemoveFromSelectedField';
|
||||
import store from 'store';
|
||||
import {
|
||||
UPDATE_INTERESTING_FIELDS,
|
||||
UPDATE_SELECTED_FIELDS,
|
||||
} from 'types/actions/logs';
|
||||
|
||||
import { RESTRICTED_SELECTED_FIELDS } from './config';
|
||||
import { OnHandleAddInterestProps, OnHandleRemoveInterestProps } from './types';
|
||||
|
||||
export const onHandleAddInterest = async ({
|
||||
setInterestingFieldLoading,
|
||||
fieldIndex,
|
||||
fieldData,
|
||||
interesting,
|
||||
interestingFieldLoading,
|
||||
selected,
|
||||
}: OnHandleAddInterestProps): Promise<void> => {
|
||||
const { dispatch } = store;
|
||||
|
||||
setInterestingFieldLoading((prevState: number[]) => {
|
||||
prevState.push(fieldIndex);
|
||||
return [...prevState];
|
||||
});
|
||||
|
||||
await AddToSelectedFields({
|
||||
...fieldData,
|
||||
selected: true,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_INTERESTING_FIELDS,
|
||||
payload: {
|
||||
field: interesting.filter((e) => e.name !== fieldData.name),
|
||||
type: 'selected',
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_SELECTED_FIELDS,
|
||||
payload: {
|
||||
field: [...selected, fieldData],
|
||||
type: 'selected',
|
||||
},
|
||||
});
|
||||
|
||||
setInterestingFieldLoading(
|
||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
||||
);
|
||||
};
|
||||
|
||||
export const onHandleRemoveInterest = async ({
|
||||
setSelectedFieldLoading,
|
||||
selected,
|
||||
interesting,
|
||||
interestingFieldLoading,
|
||||
fieldData,
|
||||
fieldIndex,
|
||||
}: OnHandleRemoveInterestProps): Promise<void> => {
|
||||
if (RESTRICTED_SELECTED_FIELDS.includes(fieldData.name)) return;
|
||||
|
||||
const { dispatch } = store;
|
||||
|
||||
setSelectedFieldLoading((prevState) => {
|
||||
prevState.push(fieldIndex);
|
||||
return [...prevState];
|
||||
});
|
||||
|
||||
await RemoveSelectedField({
|
||||
...fieldData,
|
||||
selected: false,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_SELECTED_FIELDS,
|
||||
payload: {
|
||||
field: selected.filter((e) => e.name !== fieldData.name),
|
||||
type: 'selected',
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_INTERESTING_FIELDS,
|
||||
payload: {
|
||||
field: [...interesting, fieldData],
|
||||
type: 'interesting',
|
||||
},
|
||||
});
|
||||
|
||||
setSelectedFieldLoading(
|
||||
interestingFieldLoading.filter((e) => e !== fieldIndex),
|
||||
);
|
||||
};
|
@ -1,8 +1,9 @@
|
||||
import { Input, InputRef, Popover } from 'antd';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import getStep from 'lib/getStep';
|
||||
import { debounce } from 'lodash-es';
|
||||
import debounce from 'lodash-es/debounce';
|
||||
import React, {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
@ -12,6 +13,7 @@ import React, {
|
||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { GetLogsFields } from 'store/actions/logs/getFields';
|
||||
import { getLogs } from 'store/actions/logs/getLogs';
|
||||
import { getLogsAggregate } from 'store/actions/logs/getLogsAggregate';
|
||||
import { AppState } from 'store/reducers';
|
||||
@ -32,6 +34,7 @@ import { useSearchParser } from './useSearchParser';
|
||||
function SearchFilter({
|
||||
getLogs,
|
||||
getLogsAggregate,
|
||||
getLogsFields,
|
||||
}: SearchFilterProps): JSX.Element {
|
||||
const {
|
||||
updateParsedQuery,
|
||||
@ -45,7 +48,7 @@ function SearchFilter({
|
||||
AppState,
|
||||
ILogsReducer
|
||||
>((state) => state.logs);
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
@ -69,6 +72,8 @@ function SearchFilter({
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(customQuery: string) => {
|
||||
getLogsFields();
|
||||
|
||||
if (liveTail === 'PLAYING') {
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
@ -77,15 +82,13 @@ function SearchFilter({
|
||||
dispatch({
|
||||
type: FLUSH_LOGS,
|
||||
});
|
||||
setTimeout(
|
||||
() =>
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
payload: liveTail,
|
||||
}),
|
||||
0,
|
||||
);
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
payload: liveTail,
|
||||
});
|
||||
} else {
|
||||
const { maxTime, minTime } = globalTime;
|
||||
|
||||
getLogs({
|
||||
q: customQuery,
|
||||
limit: logLinesPerPage,
|
||||
@ -117,8 +120,8 @@ function SearchFilter({
|
||||
idStart,
|
||||
liveTail,
|
||||
logLinesPerPage,
|
||||
maxTime,
|
||||
minTime,
|
||||
globalTime,
|
||||
getLogsFields,
|
||||
],
|
||||
);
|
||||
|
||||
@ -145,12 +148,12 @@ function SearchFilter({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
urlQueryString,
|
||||
maxTime,
|
||||
minTime,
|
||||
idEnd,
|
||||
idStart,
|
||||
logLinesPerPage,
|
||||
dispatch,
|
||||
globalTime.maxTime,
|
||||
globalTime.minTime,
|
||||
]);
|
||||
|
||||
return (
|
||||
@ -194,6 +197,7 @@ function SearchFilter({
|
||||
interface DispatchProps {
|
||||
getLogs: typeof getLogs;
|
||||
getLogsAggregate: typeof getLogsAggregate;
|
||||
getLogsFields: typeof GetLogsFields;
|
||||
}
|
||||
|
||||
type SearchFilterProps = DispatchProps;
|
||||
@ -203,6 +207,7 @@ const mapDispatchToProps = (
|
||||
): DispatchProps => ({
|
||||
getLogs: bindActionCreators(getLogs, dispatch),
|
||||
getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch),
|
||||
getLogsFields: bindActionCreators(GetLogsFields, dispatch),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(SearchFilter);
|
||||
export default connect(null, mapDispatchToProps)(memo(SearchFilter));
|
||||
|
@ -1,38 +1,54 @@
|
||||
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
|
||||
import history from 'lib/history';
|
||||
import { parseQuery, reverseParser } from 'lib/logql';
|
||||
import { ILogQLParsedQueryItem } from 'lib/logql/types';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppActions from 'types/actions';
|
||||
import {
|
||||
SET_SEARCH_QUERY_PARSED_PAYLOAD,
|
||||
SET_SEARCH_QUERY_STRING,
|
||||
} from 'types/actions/logs';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
import { getGlobalTime } from './utils';
|
||||
|
||||
export function useSearchParser(): {
|
||||
queryString: string;
|
||||
parsedQuery: unknown;
|
||||
updateParsedQuery: (arg0: ILogQLParsedQueryItem[]) => void;
|
||||
updateQueryString: (arg0: string) => void;
|
||||
} {
|
||||
const dispatch = useDispatch();
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
const {
|
||||
searchFilter: { parsedQuery, queryString },
|
||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||
const { minTime, maxTime, selectedTime } = useSelector<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((store) => store.globalTime);
|
||||
|
||||
const updateQueryString = useCallback(
|
||||
(updatedQueryString: string) => {
|
||||
history.replace({
|
||||
pathname: history.location.pathname,
|
||||
search: updatedQueryString ? `?q=${updatedQueryString}` : '',
|
||||
search: `?q=${updatedQueryString}`,
|
||||
});
|
||||
|
||||
const globalTime = getMinMax(selectedTime, minTime, maxTime);
|
||||
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_STRING,
|
||||
payload: updatedQueryString,
|
||||
payload: {
|
||||
searchQueryString: updatedQueryString,
|
||||
globalTime: getGlobalTime(selectedTime, globalTime),
|
||||
},
|
||||
});
|
||||
|
||||
const parsedQueryFromString = parseQuery(updatedQueryString);
|
||||
if (!isEqual(parsedQuery, parsedQueryFromString)) {
|
||||
dispatch({
|
||||
@ -41,11 +57,13 @@ export function useSearchParser(): {
|
||||
});
|
||||
}
|
||||
},
|
||||
// need to hide this warning as we don't want to update the query string on every change
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[dispatch, parsedQuery],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (queryString !== null) updateQueryString(queryString);
|
||||
updateQueryString(queryString);
|
||||
}, [queryString, updateQueryString]);
|
||||
|
||||
const updateParsedQuery = useCallback(
|
||||
@ -61,7 +79,9 @@ export function useSearchParser(): {
|
||||
) {
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_STRING,
|
||||
payload: reversedParsedQuery,
|
||||
payload: {
|
||||
searchQueryString: reversedParsedQuery,
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
12
frontend/src/container/LogsSearchFilter/utils.ts
Normal file
12
frontend/src/container/LogsSearchFilter/utils.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
import { GetMinMaxPayload } from 'lib/getMinMax';
|
||||
|
||||
export const getGlobalTime = (
|
||||
selectedTime: Time,
|
||||
globalTime: GetMinMaxPayload,
|
||||
): GetMinMaxPayload | undefined => {
|
||||
if (selectedTime === 'custom') {
|
||||
return undefined;
|
||||
}
|
||||
return globalTime;
|
||||
};
|
@ -1,7 +1,7 @@
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
|
||||
export function FlatLogData(log: ILog): Record<string, unknown> {
|
||||
const flattenLogObject: Record<string, unknown> = {};
|
||||
export function FlatLogData(log: ILog): Record<string, string> {
|
||||
const flattenLogObject: Record<string, string> = {};
|
||||
|
||||
Object.keys(log).forEach((key: string): void => {
|
||||
if (typeof log[key as never] !== 'object') {
|
||||
|
@ -6,39 +6,11 @@ import LogsAggregate from 'container/LogsAggregate';
|
||||
import LogsFilters from 'container/LogsFilters';
|
||||
import LogsSearchFilter from 'container/LogsSearchFilter';
|
||||
import LogsTable from 'container/LogsTable';
|
||||
import useMountedState from 'hooks/useMountedState';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import React, { memo, useEffect } from 'react';
|
||||
import { connect, useDispatch } from 'react-redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { ThunkDispatch } from 'redux-thunk';
|
||||
import { GetLogsFields } from 'store/actions/logs/getFields';
|
||||
import AppActions from 'types/actions';
|
||||
import { SET_SEARCH_QUERY_STRING } from 'types/actions/logs';
|
||||
import React from 'react';
|
||||
|
||||
import SpaceContainer from './styles';
|
||||
|
||||
function Logs({ getLogsFields }: LogsProps): JSX.Element {
|
||||
const getMountedState = useMountedState();
|
||||
|
||||
const urlQuery = useUrlQuery();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
const hasMounted = getMountedState();
|
||||
|
||||
if (!hasMounted) {
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_STRING,
|
||||
payload: urlQuery.get('q'),
|
||||
});
|
||||
}
|
||||
}, [dispatch, getMountedState, urlQuery]);
|
||||
|
||||
useEffect(() => {
|
||||
getLogsFields();
|
||||
}, [getLogsFields]);
|
||||
|
||||
function Logs(): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<SpaceContainer
|
||||
@ -63,16 +35,4 @@ function Logs({ getLogsFields }: LogsProps): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
type LogsProps = DispatchProps;
|
||||
|
||||
interface DispatchProps {
|
||||
getLogsFields: () => (dispatch: Dispatch<AppActions>) => void;
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (
|
||||
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
||||
): DispatchProps => ({
|
||||
getLogsFields: bindActionCreators(GetLogsFields, dispatch),
|
||||
});
|
||||
|
||||
export default connect(null, mapDispatchToProps)(memo(Logs));
|
||||
export default Logs;
|
||||
|
@ -6,6 +6,10 @@ import {
|
||||
UPDATE_AUTO_REFRESH_INTERVAL,
|
||||
UPDATE_TIME_INTERVAL,
|
||||
} from 'types/actions/globalTime';
|
||||
import {
|
||||
RESET_ID_START_AND_END,
|
||||
SET_SEARCH_QUERY_STRING,
|
||||
} from 'types/actions/logs';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
const intitalState: GlobalReducer = {
|
||||
@ -53,6 +57,29 @@ const globalTimeReducer = (
|
||||
};
|
||||
}
|
||||
|
||||
case RESET_ID_START_AND_END: {
|
||||
return {
|
||||
...state,
|
||||
maxTime: action.payload.maxTime,
|
||||
minTime: action.payload.minTime,
|
||||
};
|
||||
}
|
||||
|
||||
case SET_SEARCH_QUERY_STRING: {
|
||||
const { globalTime } = action.payload;
|
||||
if (globalTime) {
|
||||
return {
|
||||
...state,
|
||||
maxTime: globalTime.maxTime,
|
||||
minTime: globalTime.minTime,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ import {
|
||||
SET_SEARCH_QUERY_STRING,
|
||||
STOP_LIVE_TAIL,
|
||||
TOGGLE_LIVE_TAIL,
|
||||
UPDATE_INTERESTING_FIELDS,
|
||||
UPDATE_SELECTED_FIELDS,
|
||||
} from 'types/actions/logs';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
@ -83,7 +85,7 @@ export const LogsReducer = (
|
||||
...state,
|
||||
searchFilter: {
|
||||
...state.searchFilter,
|
||||
queryString: action.payload,
|
||||
queryString: action.payload.searchQueryString,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -126,7 +128,7 @@ export const LogsReducer = (
|
||||
case SET_LOG_LINES_PER_PAGE: {
|
||||
return {
|
||||
...state,
|
||||
logLinesPerPage: action.payload,
|
||||
logLinesPerPage: action.payload.logsLinesPerPage,
|
||||
};
|
||||
}
|
||||
|
||||
@ -203,6 +205,26 @@ export const LogsReducer = (
|
||||
};
|
||||
}
|
||||
|
||||
case UPDATE_INTERESTING_FIELDS: {
|
||||
return {
|
||||
...state,
|
||||
fields: {
|
||||
...state.fields,
|
||||
interesting: action.payload.field,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
case UPDATE_SELECTED_FIELDS: {
|
||||
return {
|
||||
...state,
|
||||
fields: {
|
||||
...state.fields,
|
||||
selected: action.payload.field,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
import { Time } from 'container/TopNav/DateTimeSelection/config';
|
||||
|
||||
import { ResetIdStartAndEnd, SetSearchQueryString } from './logs';
|
||||
|
||||
export const UPDATE_TIME_INTERVAL = 'UPDATE_TIME_INTERVAL';
|
||||
export const GLOBAL_TIME_LOADING_START = 'GLOBAL_TIME_LOADING_START';
|
||||
export const UPDATE_AUTO_REFRESH_DISABLED = 'UPDATE_AUTO_REFRESH_DISABLED';
|
||||
@ -37,4 +39,6 @@ export type GlobalTimeAction =
|
||||
| UpdateTimeInterval
|
||||
| GlobalTimeLoading
|
||||
| UpdateAutoRefreshDisabled
|
||||
| UpdateAutoRefreshInterval;
|
||||
| UpdateAutoRefreshInterval
|
||||
| ResetIdStartAndEnd
|
||||
| SetSearchQueryString;
|
||||
|
@ -1,26 +1,11 @@
|
||||
// import { DBOverView } from 'types/api/metrics/getDBOverview';
|
||||
// import { ExternalAverageDuration } from 'types/api/metrics/getExternalAverageDuration';
|
||||
// import { ExternalError } from 'types/api/metrics/getExternalError';
|
||||
// import { ExternalService } from 'types/api/metrics/getExternalService';
|
||||
// import { IResourceAttributeQuery } from 'container/MetricsApplication/ResourceAttributesFilter/types';
|
||||
// import { ServicesList } from 'types/api/metrics/getService';
|
||||
// import { ServiceOverview } from 'types/api/metrics/getServiceOverview';
|
||||
// import { TopEndPoints } from 'types/api/metrics/getTopEndPoints';
|
||||
|
||||
import { ILogQLParsedQueryItem } from 'lib/logql/types';
|
||||
import { IFieldMoveToSelected, IFields } from 'types/api/logs/fields';
|
||||
import { IField, IFieldMoveToSelected, IFields } from 'types/api/logs/fields';
|
||||
import { TLogsLiveTailState } from 'types/api/logs/liveTail';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { ILogsAggregate } from 'types/api/logs/logAggregate';
|
||||
|
||||
// export const GET_SERVICE_LIST_SUCCESS = 'GET_SERVICE_LIST_SUCCESS';
|
||||
// export const GET_SERVICE_LIST_LOADING_START = 'GET_SERVICE_LIST_LOADING_START';
|
||||
// export const GET_SERVICE_LIST_ERROR = 'GET_SERVICE_LIST_ERROR';
|
||||
// export const GET_INITIAL_APPLICATION_LOADING =
|
||||
// 'GET_INITIAL_APPLICATION_LOADING';
|
||||
// export const GET_INITIAL_APPLICATION_ERROR = 'GET_INITIAL_APPLICATION_ERROR';
|
||||
// export const GET_INTIAL_APPLICATION_DATA = 'GET_INTIAL_APPLICATION_DATA';
|
||||
// export const RESET_INITIAL_APPLICATION_DATA = 'RESET_INITIAL_APPLICATION_DATA';
|
||||
import { GlobalTime } from './globalTime';
|
||||
|
||||
export const GET_FIELDS = 'LOGS_GET_FIELDS';
|
||||
export const SET_FIELDS = 'LOGS_SET_FIELDS';
|
||||
export const SET_SEARCH_QUERY_STRING = 'LOGS_SET_SEARCH_QUERY_STRING';
|
||||
@ -43,6 +28,9 @@ export const PUSH_LIVE_TAIL_EVENT = 'LOGS_PUSH_LIVE_TAIL_EVENT';
|
||||
export const STOP_LIVE_TAIL = 'LOGS_STOP_LIVE_TAIL';
|
||||
export const FLUSH_LOGS = 'LOGS_FLUSH_LOGS';
|
||||
export const SET_LIVE_TAIL_START_TIME = 'LOGS_SET_LIVE_TAIL_START_TIME';
|
||||
export const UPDATE_SELECTED_FIELDS = 'LOGS_UPDATE_SELECTED_FIELDS';
|
||||
export const UPDATE_INTERESTING_FIELDS = 'LOGS_UPDATE_INTERESTING_FIELDS';
|
||||
|
||||
export interface GetFields {
|
||||
type: typeof GET_FIELDS;
|
||||
}
|
||||
@ -53,7 +41,10 @@ export interface SetFields {
|
||||
}
|
||||
export interface SetSearchQueryString {
|
||||
type: typeof SET_SEARCH_QUERY_STRING;
|
||||
payload: string;
|
||||
payload: {
|
||||
searchQueryString: string;
|
||||
globalTime?: GlobalTime;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SetSearchQueryParsedPayload {
|
||||
@ -75,7 +66,9 @@ export interface UpdateLogs {
|
||||
}
|
||||
export interface SetLogsLinesPerPage {
|
||||
type: typeof SET_LOG_LINES_PER_PAGE;
|
||||
payload: number;
|
||||
payload: {
|
||||
logsLinesPerPage: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PreviousLogsLines {
|
||||
@ -86,6 +79,7 @@ export interface NextLogsLines {
|
||||
}
|
||||
export interface ResetIdStartAndEnd {
|
||||
type: typeof RESET_ID_START_AND_END;
|
||||
payload: GlobalTime;
|
||||
}
|
||||
export interface SetLoading {
|
||||
type: typeof SET_LOADING;
|
||||
@ -124,6 +118,16 @@ export interface SetLiveTailStartTime {
|
||||
payload: number;
|
||||
}
|
||||
|
||||
type IFieldType = 'interesting' | 'selected';
|
||||
|
||||
export interface UpdateSelectedInterestFields {
|
||||
type: typeof UPDATE_INTERESTING_FIELDS | typeof UPDATE_SELECTED_FIELDS;
|
||||
payload: {
|
||||
field: IField[];
|
||||
type: IFieldType;
|
||||
};
|
||||
}
|
||||
|
||||
export type LogsActions =
|
||||
| GetFields
|
||||
| SetFields
|
||||
@ -144,4 +148,5 @@ export type LogsActions =
|
||||
| PushLiveTailEvent
|
||||
| StopLiveTail
|
||||
| FlushLogs
|
||||
| SetLiveTailStartTime;
|
||||
| SetLiveTailStartTime
|
||||
| UpdateSelectedInterestFields;
|
||||
|
Loading…
x
Reference in New Issue
Block a user