diff --git a/frontend/src/container/LogControls/styles.ts b/frontend/src/container/LogControls/styles.ts index 304b443c54..f91bc43363 100644 --- a/frontend/src/container/LogControls/styles.ts +++ b/frontend/src/container/LogControls/styles.ts @@ -5,5 +5,4 @@ export const Container = styled.div` align-items: center; justify-content: flex-end; gap: 0.5rem; - margin-bottom: 0.5rem; `; diff --git a/frontend/src/container/LogLiveTail/OptionIcon.tsx b/frontend/src/container/LogLiveTail/OptionIcon.tsx deleted file mode 100644 index 9a6f935ed9..0000000000 --- a/frontend/src/container/LogLiveTail/OptionIcon.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; - -interface OptionIconProps { - isDarkMode: boolean; -} -function OptionIcon({ isDarkMode }: OptionIconProps): JSX.Element { - return ( - - - - ); -} - -export default OptionIcon; diff --git a/frontend/src/container/LogLiveTail/config.ts b/frontend/src/container/LogLiveTail/config.ts new file mode 100644 index 0000000000..27ee168205 --- /dev/null +++ b/frontend/src/container/LogLiveTail/config.ts @@ -0,0 +1,26 @@ +export const TIME_PICKER_OPTIONS = [ + { + value: 5, + label: '5m', + }, + { + value: 15, + label: '15m', + }, + { + value: 30, + label: '30m', + }, + { + value: 60, + label: '1hr', + }, + { + value: 360, + label: '6hrs', + }, + { + value: 720, + label: '12hrs', + }, +]; diff --git a/frontend/src/container/LogLiveTail/index.tsx b/frontend/src/container/LogLiveTail/index.tsx index 315c629828..1c48b4d8ab 100644 --- a/frontend/src/container/LogLiveTail/index.tsx +++ b/frontend/src/container/LogLiveTail/index.tsx @@ -1,7 +1,10 @@ -/* eslint-disable react-hooks/exhaustive-deps */ import { green } from '@ant-design/colors'; -import { PauseOutlined, PlayCircleOutlined } from '@ant-design/icons'; -import { Button, Popover, Row, Select } from 'antd'; +import { + MoreOutlined, + PauseOutlined, + PlayCircleOutlined, +} from '@ant-design/icons'; +import { Button, Popover, Select, Space } from 'antd'; import { LiveTail } from 'api/logs/livetail'; import dayjs from 'dayjs'; import { throttle } from 'lodash-es'; @@ -18,38 +21,11 @@ import { TLogsLiveTailState } from 'types/api/logs/liveTail'; import AppReducer from 'types/reducer/app'; import { ILogsReducer } from 'types/reducer/logs'; -import OptionIcon from './OptionIcon'; -import { TimePickerCard, TimePickerSelect } from './styles'; +import { TIME_PICKER_OPTIONS } from './config'; +import { StopContainer, TimePickerCard, TimePickerSelect } from './styles'; const { Option } = Select; -const TIME_PICKER_OPTIONS = [ - { - value: 5, - label: '5m', - }, - { - value: 15, - label: '15m', - }, - { - value: 30, - label: '30m', - }, - { - value: 60, - label: '1hr', - }, - { - value: 360, - label: '6hrs', - }, - { - value: 720, - label: '12hrs', - }, -]; - function LogLiveTail(): JSX.Element { const { liveTail, @@ -75,14 +51,12 @@ function LogLiveTail(): JSX.Element { type: PUSH_LIVE_TAIL_EVENT, payload: batchedEventsRef.current.reverse(), }); - // console.log('DISPATCH', batchedEventsRef.current.length); batchedEventsRef.current = []; }, 1500), [], ); const batchLiveLog = (e: { data: string }): void => { - // console.log('EVENT BATCHED'); batchedEventsRef.current.push(JSON.parse(e.data as string) as never); pushLiveLog(); }; @@ -123,6 +97,7 @@ function LogLiveTail(): JSX.Element { if (liveTail === 'STOPPED') { liveTailSourceRef.current = null; } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [liveTail]); const handleLiveTailStart = (): void => { @@ -155,47 +130,39 @@ function LogLiveTail(): JSX.Element { ), [dispatch, liveTail, liveTailStartRange], ); + return ( - - - {liveTail === 'PLAYING' ? ( - handleLiveTail('PAUSED')} - title="Pause live tail" - style={{ background: green[6] }} - > - Pause - - ) : ( - - Go Live - - )} - {liveTail !== 'STOPPED' && ( - handleLiveTail('STOPPED')} - title="Exit live tail" - > - - - )} - + + {liveTail === 'PLAYING' ? ( + handleLiveTail('PAUSED')} + title="Pause live tail" + style={{ background: green[6] }} + > + Pause + + + ) : ( + + Go Live + + )} + + {liveTail !== 'STOPPED' && ( + handleLiveTail('STOPPED')} + title="Exit live tail" + > + + + )} - - - + - + ); } diff --git a/frontend/src/container/LogLiveTail/styles.ts b/frontend/src/container/LogLiveTail/styles.ts index 4e8691dbd0..66081585b3 100644 --- a/frontend/src/container/LogLiveTail/styles.ts +++ b/frontend/src/container/LogLiveTail/styles.ts @@ -3,6 +3,7 @@ import styled from 'styled-components'; export const TimePickerCard = styled(Card)` .ant-card-body { + display: flex; padding: 0; } `; @@ -10,3 +11,15 @@ export const TimePickerCard = styled(Card)` export const TimePickerSelect = styled(Select)` min-width: 100px; `; + +interface Props { + isDarkMode: boolean; +} + +export const StopContainer = styled.div` + height: 0.8rem; + width: 0.8rem; + border-radius: 0.1rem; + background-color: ${({ isDarkMode }): string => + isDarkMode ? '#fff' : '#000'}; +`; diff --git a/frontend/src/container/Logs/index.tsx b/frontend/src/container/Logs/index.tsx deleted file mode 100644 index 9139d7c9e2..0000000000 --- a/frontend/src/container/Logs/index.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { Divider, Row } from 'antd'; -import LogControls from 'container/LogControls'; -import LogDetailedView from 'container/LogDetailedView'; -import LogLiveTail from 'container/LogLiveTail'; -import LogsAggregate from 'container/LogsAggregate'; -import LogsFilters from 'container/LogsFilters'; -import SearchFilter from 'container/LogsSearchFilter'; -import LogsTable from 'container/LogsTable'; -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'; - -function Logs({ getLogsFields }: LogsProps): JSX.Element { - const urlQuery = useUrlQuery(); - - const dispatch = useDispatch(); - - useEffect(() => { - dispatch({ - type: SET_SEARCH_QUERY_STRING, - payload: urlQuery.get('q'), - }); - }, [dispatch, urlQuery]); - - useEffect(() => { - getLogsFields(); - }, [getLogsFields]); - - return ( - - - - - - - - - - - - - - - - - ); -} - -type LogsProps = DispatchProps; - -interface DispatchProps { - getLogsFields: () => (dispatch: Dispatch) => void; -} - -const mapDispatchToProps = ( - dispatch: ThunkDispatch, -): DispatchProps => ({ - getLogsFields: bindActionCreators(GetLogsFields, dispatch), -}); - -export default connect(null, mapDispatchToProps)(memo(Logs)); diff --git a/frontend/src/container/LogsAggregate/index.tsx b/frontend/src/container/LogsAggregate/index.tsx index 5b7c330135..737164f9fd 100644 --- a/frontend/src/container/LogsAggregate/index.tsx +++ b/frontend/src/container/LogsAggregate/index.tsx @@ -1,4 +1,3 @@ -/* eslint-disable react-hooks/exhaustive-deps */ import { blue } from '@ant-design/colors'; import Graph from 'components/Graph'; import Spinner from 'components/Spinner'; @@ -16,9 +15,6 @@ import { ILogsReducer } from 'types/reducer/logs'; import { Container } from './styles'; -interface LogsAggregateProps { - getLogsAggregate: (arg0: Parameters[0]) => void; -} function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element { const { searchFilter: { queryString }, @@ -42,18 +38,18 @@ function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element { clearInterval(reFetchIntervalRef.current); } reFetchIntervalRef.current = null; - getLogsAggregate({ - timestampStart: minTime, - timestampEnd: maxTime, - step: getStep({ - start: minTime, - end: maxTime, - inputFormat: 'ns', - }), - q: queryString, - ...(idStart ? { idGt: idStart } : {}), - ...(idEnd ? { idLt: idEnd } : {}), - }); + // getLogsAggregate({ + // timestampStart: minTime, + // timestampEnd: maxTime, + // step: getStep({ + // start: minTime, + // end: maxTime, + // inputFormat: 'ns', + // }), + // q: queryString, + // ...(idStart ? { idGt: idStart } : {}), + // ...(idEnd ? { idLt: idEnd } : {}), + // }); break; } @@ -89,18 +85,9 @@ function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element { break; } } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [getLogsAggregate, maxTime, minTime, liveTail]); - const data = { - labels: logsAggregate.map((s) => new Date(s.timestamp / 1000000)), - datasets: [ - { - data: logsAggregate.map((s) => s.value), - backgroundColor: blue[4], - }, - ], - }; - return ( {isLoadingAggregate ? ( @@ -108,7 +95,15 @@ function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element { ) : ( new Date(s.timestamp / 1000000)), + datasets: [ + { + data: logsAggregate.map((s) => s.value), + backgroundColor: blue[4], + }, + ], + }} type="bar" containerHeight="100%" animate={false} @@ -118,6 +113,10 @@ function LogsAggregate({ getLogsAggregate }: LogsAggregateProps): JSX.Element { ); } +interface LogsAggregateProps { + getLogsAggregate: (arg0: Parameters[0]) => void; +} + interface DispatchProps { getLogsAggregate: ( props: Parameters[0], diff --git a/frontend/src/container/LogsFilters/index.tsx b/frontend/src/container/LogsFilters/index.tsx index 28480edccc..42d8bc500e 100644 --- a/frontend/src/container/LogsFilters/index.tsx +++ b/frontend/src/container/LogsFilters/index.tsx @@ -21,9 +21,6 @@ import { CategoryContainer, Container, FieldContainer } from './styles'; const RESTRICTED_SELECTED_FIELDS = ['timestamp', 'id']; -interface LogsFiltersProps { - getLogsFields: () => void; -} function LogsFilters({ getLogsFields }: LogsFiltersProps): JSX.Element { const { fields: { interesting, selected }, @@ -150,4 +147,6 @@ const mapDispatchToProps = ( getLogsFields: bindActionCreators(GetLogsFields, dispatch), }); +type LogsFiltersProps = DispatchProps; + export default connect(null, mapDispatchToProps)(memo(LogsFilters)); diff --git a/frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/QueryBuilder.tsx b/frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/QueryBuilder.tsx index 0723d2378e..6744c234da 100644 --- a/frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/QueryBuilder.tsx +++ b/frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/QueryBuilder.tsx @@ -4,7 +4,7 @@ /* eslint-disable no-param-reassign */ /* eslint-disable react/no-array-index-key */ /* eslint-disable react-hooks/exhaustive-deps */ -import { CloseOutlined } from '@ant-design/icons'; +import { CloseOutlined, CloseSquareOutlined } from '@ant-design/icons'; import { Button, Input, Select } from 'antd'; import CategoryHeading from 'components/Logs/CategoryHeading'; import { @@ -19,12 +19,46 @@ import { AppState } from 'store/reducers'; import { ILogsReducer } from 'types/reducer/logs'; import { v4 } from 'uuid'; +import { SearchFieldsProps } from '..'; import FieldKey from '../FieldKey'; import { QueryConditionContainer, QueryFieldContainer } from '../styles'; import { createParsedQueryStructure } from '../utils'; +import { Container, QueryWrapper } from './styles'; import { hashCode, parseQuery } from './utils'; const { Option } = Select; + +function QueryConditionField({ + query, + queryIndex, + onUpdate, + style, +}: QueryConditionFieldProps): JSX.Element { + return ( + + { + onUpdate({ ...query, value: e }, queryIndex); + }} + > + {Object.values(ConditionalOperators).map((cond) => ( + + {cond} + + ))} + + + ); +} + +QueryConditionField.defaultProps = { + style: undefined, +}; + interface QueryFieldProps { query: Query; queryIndex: number; @@ -140,41 +174,15 @@ interface QueryConditionFieldProps { query: { value: string | string[]; type: string }[]; queryIndex: number; onUpdate: (arg0: unknown, arg1: number) => void; -} -function QueryConditionField({ - query, - queryIndex, - onUpdate, -}: QueryConditionFieldProps): JSX.Element { - return ( - - { - onUpdate({ ...query, value: e }, queryIndex); - }} - style={{ width: '100%' }} - > - {Object.values(ConditionalOperators).map((cond) => ( - - {cond} - - ))} - - - ); + style?: React.CSSProperties; } export type Query = { value: string | string[]; type: string }[]; function QueryBuilder({ updateParsedQuery, -}: { - updateParsedQuery: (arg0: unknown) => void; -}): JSX.Element { + onDropDownToggleHandler, +}: SearchFieldsProps): JSX.Element { const { searchFilter: { parsedQuery }, } = useSelector((store) => store.logs); @@ -233,19 +241,16 @@ function QueryBuilder({ /> ); }); + return ( - - LOG QUERY BUILDER - - {QueryUI()} - - + <> + + LOG QUERY BUILDER + + + + {QueryUI()} + > ); } diff --git a/frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/styles.ts b/frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/styles.ts new file mode 100644 index 0000000000..211a5b6407 --- /dev/null +++ b/frontend/src/container/LogsSearchFilter/SearchFields/QueryBuilder/styles.ts @@ -0,0 +1,17 @@ +import styled from 'styled-components'; + +interface Props { + isMargin: boolean; +} +export const Container = styled.div` + display: flex; + justify-content: space-between; + width: 100%; + margin-bottom: ${(props): string => (props.isMargin ? '2rem' : '0')}; +`; + +export const QueryWrapper = styled.div` + display: grid; + grid-template-columns: 80px 1fr; + margin: 0.5rem 0px; +`; diff --git a/frontend/src/container/LogsSearchFilter/SearchFields/Suggestions.tsx b/frontend/src/container/LogsSearchFilter/SearchFields/Suggestions.tsx index c80b996e48..838d790954 100644 --- a/frontend/src/container/LogsSearchFilter/SearchFields/Suggestions.tsx +++ b/frontend/src/container/LogsSearchFilter/SearchFields/Suggestions.tsx @@ -1,6 +1,6 @@ import { Button } from 'antd'; import CategoryHeading from 'components/Logs/CategoryHeading'; -import { map } from 'lodash-es'; +import map from 'lodash-es/map'; import React from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; diff --git a/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx b/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx index 888257279f..9080c454ab 100644 --- a/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx +++ b/frontend/src/container/LogsSearchFilter/SearchFields/index.tsx @@ -2,14 +2,23 @@ import React from 'react'; import QueryBuilder from './QueryBuilder/QueryBuilder'; import Suggestions from './Suggestions'; +import { QueryFields } from './utils'; -interface SearchFieldsProps { - updateParsedQuery: () => void; +export interface SearchFieldsProps { + updateParsedQuery: (query: QueryFields[]) => void; + onDropDownToggleHandler: (value: boolean) => VoidFunction; } -function SearchFields({ updateParsedQuery }: SearchFieldsProps): JSX.Element { + +function SearchFields({ + updateParsedQuery, + onDropDownToggleHandler, +}: SearchFieldsProps): JSX.Element { return ( <> - + > ); diff --git a/frontend/src/container/LogsSearchFilter/SearchFields/styles.tsx b/frontend/src/container/LogsSearchFilter/SearchFields/styles.tsx index 3ec67d2fd7..1f06e924ca 100644 --- a/frontend/src/container/LogsSearchFilter/SearchFields/styles.tsx +++ b/frontend/src/container/LogsSearchFilter/SearchFields/styles.tsx @@ -9,12 +9,13 @@ export const QueryFieldContainer = styled.div` align-items: center; border-radius: 0.25rem; gap: 1rem; + width: 100%; &:hover { background: ${blue[6]}; } `; export const QueryConditionContainer = styled.div` - padding: 0.25rem 0rem; - margin: 0.1rem 0; + display: flex; + flex-direction: row; `; diff --git a/frontend/src/container/LogsSearchFilter/index.tsx b/frontend/src/container/LogsSearchFilter/index.tsx index 6c3d43d874..5af48b17c1 100644 --- a/frontend/src/container/LogsSearchFilter/index.tsx +++ b/frontend/src/container/LogsSearchFilter/index.tsx @@ -1,11 +1,8 @@ -/* eslint-disable react-hooks/exhaustive-deps */ -import { CloseSquareOutlined } from '@ant-design/icons'; -import { Button, Input } from 'antd'; -import useClickOutside from 'hooks/useClickOutside'; +import { Input, InputRef, Popover } from 'antd'; +import useUrlQuery from 'hooks/useUrlQuery'; import getStep from 'lib/getStep'; -import React, { memo, useEffect, useMemo, useRef, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import { connect, useDispatch, useSelector } from 'react-redux'; -import { useLocation } from 'react-use'; import { bindActionCreators, Dispatch } from 'redux'; import { ThunkDispatch } from 'redux-thunk'; import { getLogs } from 'store/actions/logs/getLogs'; @@ -17,17 +14,9 @@ import { GlobalReducer } from 'types/reducer/globalTime'; import { ILogsReducer } from 'types/reducer/logs'; import SearchFields from './SearchFields'; -import { DropDownContainer } from './styles'; +import { Container, DropDownContainer } from './styles'; import { useSearchParser } from './useSearchParser'; -const { Search } = Input; - -interface SearchFilterProps { - getLogs: (props: Parameters[0]) => ReturnType; - getLogsAggregate: ( - props: Parameters[0], - ) => ReturnType; -} function SearchFilter({ getLogs, getLogsAggregate, @@ -38,6 +27,14 @@ function SearchFilter({ updateQueryString, } = useSearchParser(); const [showDropDown, setShowDropDown] = useState(false); + const searchRef = useRef(null); + + const onDropDownToggleHandler = useCallback( + (value: boolean) => (): void => { + setShowDropDown(value); + }, + [], + ); const { logLinesPerPage, idEnd, idStart, liveTail } = useSelector< AppState, @@ -48,117 +45,104 @@ function SearchFilter({ (state) => state.globalTime, ); - const searchComponentRef = useRef(null); + const dispatch = useDispatch>(); - useClickOutside(searchComponentRef, (e: HTMLElement) => { - // using this hack as overlay span is voilating this condition - if ( - e.nodeName === 'svg' || - e.nodeName === 'path' || - e.nodeName === 'span' || - e.nodeName === 'button' - ) { - return; - } + const handleSearch = useCallback( + (customQuery) => { + if (liveTail === 'PLAYING') { + dispatch({ + type: TOGGLE_LIVE_TAIL, + payload: 'PAUSED', + }); + setTimeout( + () => + dispatch({ + type: TOGGLE_LIVE_TAIL, + payload: liveTail, + }), + 0, + ); + } else { + getLogs({ + q: customQuery, + limit: logLinesPerPage, + orderBy: 'timestamp', + order: 'desc', + timestampStart: minTime, + timestampEnd: maxTime, + ...(idStart ? { idGt: idStart } : {}), + ...(idEnd ? { idLt: idEnd } : {}), + }); - if ( - e.nodeName === 'DIV' && - ![ - 'ant-empty-image', - 'ant-select-item', - 'ant-col', - 'ant-select-item-option-content', - 'ant-select-item-option-active', - ].find((p) => p.indexOf(e.className) !== -1) && - !(e.ariaSelected === 'true') && - showDropDown - ) { - setShowDropDown(false); - } - }); - const { search } = useLocation(); - const dispatch = useDispatch(); - const handleSearch = (customQuery = ''): void => { - if (liveTail === 'PLAYING') { - dispatch({ - type: TOGGLE_LIVE_TAIL, - payload: 'PAUSED', - }); - setTimeout( - () => - dispatch({ - type: TOGGLE_LIVE_TAIL, - payload: liveTail, + getLogsAggregate({ + timestampStart: minTime, + timestampEnd: maxTime, + step: getStep({ + start: minTime, + end: maxTime, + inputFormat: 'ns', }), - 0, - ); - } else { - getLogs({ - q: customQuery || queryString, - limit: logLinesPerPage, - orderBy: 'timestamp', - order: 'desc', - timestampStart: minTime, - timestampEnd: maxTime, - ...(idStart ? { idGt: idStart } : {}), - ...(idEnd ? { idLt: idEnd } : {}), - }); + q: customQuery, + }); + } + }, + [ + dispatch, + getLogs, + getLogsAggregate, + idEnd, + idStart, + liveTail, + logLinesPerPage, + maxTime, + minTime, + ], + ); - getLogsAggregate({ - timestampStart: minTime, - timestampEnd: maxTime, - step: getStep({ - start: minTime, - end: maxTime, - inputFormat: 'ns', - }), - q: customQuery || queryString, - }); - } - setShowDropDown(false); - }; - - const urlQuery = useMemo(() => { - return new URLSearchParams(search); - }, [search]); + const urlQuery = useUrlQuery(); + const urlQueryString = urlQuery.get('q'); useEffect(() => { - const urlQueryString = urlQuery.get('q'); - if (urlQueryString !== null) handleSearch(urlQueryString); - }, []); + handleSearch(urlQueryString || ''); + }, [handleSearch, urlQueryString]); return ( - - setShowDropDown(true)} - value={queryString} - onChange={(e): void => { - updateQueryString(e.target.value); - }} - onSearch={handleSearch} - /> - - {showDropDown && ( + + - setShowDropDown(false)} - style={{ - position: 'absolute', - top: 0, - right: 0, - }} - > - - - + - )} - - + } + trigger="click" + overlayInnerStyle={{ + width: `${searchRef?.current?.input?.offsetWidth || 0}px`, + }} + visible={showDropDown} + destroyTooltipOnHide + onVisibleChange={(value): void => { + onDropDownToggleHandler(value)(); + }} + > + { + updateQueryString(e.target.value); + }} + allowClear + onSearch={handleSearch} + /> + + ); } + interface DispatchProps { getLogs: ( props: Parameters[0], @@ -168,6 +152,8 @@ interface DispatchProps { ) => (dispatch: Dispatch) => void; } +type SearchFilterProps = DispatchProps; + const mapDispatchToProps = ( dispatch: ThunkDispatch, ): DispatchProps => ({ @@ -175,4 +161,4 @@ const mapDispatchToProps = ( getLogsAggregate: bindActionCreators(getLogsAggregate, dispatch), }); -export default connect(null, mapDispatchToProps)(memo(SearchFilter)); +export default connect(null, mapDispatchToProps)(SearchFilter); diff --git a/frontend/src/container/LogsSearchFilter/styles.ts b/frontend/src/container/LogsSearchFilter/styles.ts index 640881c3fa..7b379b08da 100644 --- a/frontend/src/container/LogsSearchFilter/styles.ts +++ b/frontend/src/container/LogsSearchFilter/styles.ts @@ -2,11 +2,13 @@ import { Card } from 'antd'; import styled from 'styled-components'; export const DropDownContainer = styled(Card)` - top: 0.5rem; - position: absolute; - width: 100%; - z-index: 1; .ant-card-body { - padding: 0.8rem; + width: 100%; } `; + +export const Container = styled.div` + width: 100%; + flex: 1; + position: relative; +`; diff --git a/frontend/src/container/LogsSearchFilter/useSearchParser.ts b/frontend/src/container/LogsSearchFilter/useSearchParser.ts index 50c4cc1e35..7bb43eeea0 100644 --- a/frontend/src/container/LogsSearchFilter/useSearchParser.ts +++ b/frontend/src/container/LogsSearchFilter/useSearchParser.ts @@ -23,10 +23,12 @@ export function useSearchParser(): { const updateQueryString = useCallback( (updatedQueryString) => { - history.push({ - pathname: history.location.pathname, - search: updatedQueryString ? `?q=${updatedQueryString}` : '', - }); + if (updatedQueryString) { + history.push({ + pathname: history.location.pathname, + search: updatedQueryString ? `?q=${updatedQueryString}` : '', + }); + } dispatch({ type: SET_SEARCH_QUERY_STRING, diff --git a/frontend/src/container/LogsTable/index.tsx b/frontend/src/container/LogsTable/index.tsx index 7997fac91f..68758c59fe 100644 --- a/frontend/src/container/LogsTable/index.tsx +++ b/frontend/src/container/LogsTable/index.tsx @@ -3,48 +3,18 @@ import { Typography } from 'antd'; import LogItem from 'components/Logs/LogItem'; import Spinner from 'components/Spinner'; import { map } from 'lodash-es'; -import React, { memo, useEffect } from 'react'; -import { connect, useSelector } from 'react-redux'; -import { bindActionCreators, Dispatch } from 'redux'; -import { ThunkDispatch } from 'redux-thunk'; -import { getLogs } from 'store/actions/logs/getLogs'; +import React, { memo } from 'react'; +import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; -import AppActions from 'types/actions'; -import { GlobalReducer } from 'types/reducer/globalTime'; import { ILogsReducer } from 'types/reducer/logs'; import { Container, Heading } from './styles'; -function LogsTable({ getLogs }: LogsTableProps): JSX.Element { - const { - searchFilter: { queryString }, - logs, - logLinesPerPage, - idEnd, - idStart, - isLoading, - liveTail, - } = useSelector((state) => state.logs); - - const { maxTime, minTime } = useSelector( - (state) => state.globalTime, +function LogsTable(): JSX.Element { + const { logs, isLoading, liveTail } = useSelector( + (state) => state.logs, ); - useEffect(() => { - if (liveTail === 'STOPPED') - getLogs({ - q: queryString, - limit: logLinesPerPage, - orderBy: 'timestamp', - order: 'desc', - timestampStart: minTime, - timestampEnd: maxTime, - ...(idStart ? { idGt: idStart } : {}), - ...(idEnd ? { idLt: idEnd } : {}), - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [getLogs, idEnd, idStart, logLinesPerPage, maxTime, minTime, liveTail]); - if (isLoading) { return ; } @@ -72,20 +42,4 @@ function LogsTable({ getLogs }: LogsTableProps): JSX.Element { ); } -interface DispatchProps { - getLogs: ( - props: Parameters[0], - ) => (dispatch: Dispatch) => void; -} - -const mapDispatchToProps = ( - dispatch: ThunkDispatch, -): DispatchProps => ({ - getLogs: bindActionCreators(getLogs, dispatch), -}); - -interface LogsTableProps { - getLogs: (props: Parameters[0]) => ReturnType; -} - -export default connect(null, mapDispatchToProps)(memo(LogsTable)); +export default memo(LogsTable); diff --git a/frontend/src/pages/Logs/index.tsx b/frontend/src/pages/Logs/index.tsx index b11afff009..2799295146 100644 --- a/frontend/src/pages/Logs/index.tsx +++ b/frontend/src/pages/Logs/index.tsx @@ -1,8 +1,72 @@ -import Logs from 'container/Logs'; -import React from 'react'; +import { Divider, Row } from 'antd'; +import LogControls from 'container/LogControls'; +import LogDetailedView from 'container/LogDetailedView'; +import LogLiveTail from 'container/LogLiveTail'; +import LogsAggregate from 'container/LogsAggregate'; +import LogsFilters from 'container/LogsFilters'; +import LogsSearchFilter from 'container/LogsSearchFilter'; +import LogsTable from 'container/LogsTable'; +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'; -function LogsHome(): JSX.Element { - return ; +import SpaceContainer from './styles'; + +function Logs({ getLogsFields }: LogsProps): JSX.Element { + const urlQuery = useUrlQuery(); + + const dispatch = useDispatch(); + + useEffect(() => { + dispatch({ + type: SET_SEARCH_QUERY_STRING, + payload: urlQuery.get('q'), + }); + }, [dispatch, urlQuery]); + + useEffect(() => { + getLogsFields(); + }, [getLogsFields]); + + return ( + <> + } + align="center" + direction="horizontal" + > + + + + + + + + + + + + + + > + ); } -export default LogsHome; +type LogsProps = DispatchProps; + +interface DispatchProps { + getLogsFields: () => (dispatch: Dispatch) => void; +} + +const mapDispatchToProps = ( + dispatch: ThunkDispatch, +): DispatchProps => ({ + getLogsFields: bindActionCreators(GetLogsFields, dispatch), +}); + +export default connect(null, mapDispatchToProps)(memo(Logs)); diff --git a/frontend/src/pages/Logs/styles.ts b/frontend/src/pages/Logs/styles.ts new file mode 100644 index 0000000000..d0675ccf97 --- /dev/null +++ b/frontend/src/pages/Logs/styles.ts @@ -0,0 +1,10 @@ +import { Space } from 'antd'; +import styled from 'styled-components'; + +const SpaceContainer = styled(Space)` + .ant-space-item:nth-child(1) { + width: 100%; + } +`; + +export default SpaceContainer; diff --git a/frontend/src/store/reducers/logs.ts b/frontend/src/store/reducers/logs.ts index b082a8c022..a170de66e3 100644 --- a/frontend/src/store/reducers/logs.ts +++ b/frontend/src/store/reducers/logs.ts @@ -99,15 +99,14 @@ export const LogsReducer = ( } case ADD_SEARCH_FIELD_QUERY_STRING: { - const updatedQueryString = - state.searchFilter.queryString || - `${ - state.searchFilter.queryString && state.searchFilter.queryString.length > 0 - ? ' and ' - : '' - }${action.payload}`; + const updatedQueryString = `${state?.searchFilter?.queryString || ''}${ + state.searchFilter.queryString && state.searchFilter.queryString.length > 0 + ? ' and ' + : '' + }${action.payload}`; const updatedParsedQuery = parseQuery(updatedQueryString); + console.log({ updatedParsedQuery, updatedQueryString, action }); return { ...state, searchFilter: {