mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-15 12:21:30 +08:00

* dayjs and less loader is added * webpack config is added * moment is removed * useDebounceFunction hook is made * old components and reducer is removed * search is updated * changes are upadted for the trace page as skeleton is ready * chore: method is change from dayjs * convertObject into params is updated * initial filters are updated * initial and final filter issue is fixed * selection of the filter is updated * filters are now able to selected * checkbox disable when loading is in progress * chore: getFilter filename is updated * feat: clear all and exapanded filter is updated * chore: clearAll and expand panel is updated * feat: useClickOutSide hook is added * chore: get filter url becomes encoded * chore: get tag filters is added * feat: search tags is wip * bug: global max,min on change bug is resolved * chore: getInitial filter is updated * chore: expand panel is updated * chore: get filter is updated * chore: code smells is updated * feat: loader is added in the panel header to show the loading * chore: search tags in wip * chore: button style is updated * chore: search in wip * chore: search ui is updated from the global state * chore: search in wip * chore: search is updated * chore: getSpansAggregate section is updated * useOutside click is updated * useclickoutside hook is updated * useclickoutside hook is updated * parsing is updated * initial filter is updated * feat: trace table is updated * chore: trace table is updated * chore: useClickout side is updated for the search panel * feat: unneccesary re-render and code is removed * chore: trace table is updated * custom component is removed and used antd search component * error state is updated over search component * chore: search bar is updated * chore: left panel search and table component connection is updated * chore: trace filter config is updated * chore: for graph reducer is updated * chore: graph is updated * chore: table is updated * chore: spans is updated * chore: reducer is updated * chore: graph component is updated * chore: number of graph condition is updated * chore: input and range slider is now sync * chore: duration is updated * chore: clearAllFilter is updated * chore: duration slider is updated * chore: duration is updated and panel body loading is updated * chore: slider container is added to add padding from left to right * chore: Select filter is updated * chore: duration filter is updated * chore: Divider is added * chore: none option is added in both the dropdown * chore: icon are updated * chore: added padding in the pages component * chore: none is updated * chore: antd notification is added in the redux action * chore: some of the changes are updated * chore: display value is updated for the filter panel heading * chore: calulation is memorised * chore: utils function are updated in trace reducer * chore: getFilters are updated * tracetable is updated * chore: actions is updated * chore: metrics application is updated * chore: search on clear action is updated * chore: serviceName panel position is updated * chore: added the label in the duration * bug: edge case is fixed * chore: some more changes are updated * chore: some more changes are updated * chore: clear all is fixed * chore: panel heading caret is updated * chore: checkbox is updated * chore: isError handler is updated over initial render * chore: traces is updated * fix: tag search is updated * chore: loading is added in the trace table and soring is introduced in the trace table * bug: multiple render for the key is fixed * Bug(UI): new suggestion is updated * feat: isTraceFilterEnum function is made * bug: new changes are updated * chore: get Filter is updated * chore: application metrics params is updated * chore: error is added in the application metrics * chore: filters is updated * chore: expand panel edge case is updated * chore: expand panel is updated and utls: updateUrl function is updated * chore: reset trace state when unmounted * chore: getFilter action is updated * chore: api duration is updated * chore: useEffect dependency is updated * chore: filter is updated with the new arch * bug: trace table issue is resolved * chore: application rps url is updated for trace * chore: duration filter is updated * chore: search key is updated * chore: filter is added in the search url * bug: filter is fixed * bug: filter is fixed * bug: filter is fixed * chore: reset trace data when unmounted * chore: TopEnd point is added * chore: getInitialSpanAggregate action is updated * chore: application url is updated * chore: no tags placeholder is updated * chore: flow from customer is now fixed * chore: search is updated * chore: select all button is removed * chore: prev filter is removed to show the result * chore: config is updated * chore: checkbox component is updated * chore: span filter is updated * chore: graph issue is resolved * chore: selected is updated * chore: all filter are selected * feat: new trace page is updated * chore: utils is updated * feat: trace filter page is updated * chore: duration is now fixed * chore: duration clear filter is added * chore: onClickCheck is updated * chore: trace filter page is updated * bug: some of bugs are resolved * chore: duration body is updated * chore: topEndPoint and application query is updated * chore: user selection is updated in the duration filter * chore: panel duration is updated * chore: panel duration is updated * chore: duration bug is solved * chore: function display value is updated
313 lines
8.6 KiB
TypeScript
313 lines
8.6 KiB
TypeScript
import { Button, Select as DefaultSelect } from 'antd';
|
|
import React, { useCallback, useEffect, useState } from 'react';
|
|
|
|
import { getDefaultOption, getOptions, Time } from './config';
|
|
import { Container, Form, FormItem } from './styles';
|
|
const { Option } = DefaultSelect;
|
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
|
import setLocalStorageKey from 'api/browser/localstorage/set';
|
|
import { LOCAL_STORAGE } from 'constants/localStorage';
|
|
import getTimeString from 'lib/getTimeString';
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|
import { connect, useSelector } from 'react-redux';
|
|
import { RouteComponentProps, withRouter } from 'react-router';
|
|
import { bindActionCreators, Dispatch } from 'redux';
|
|
import { ThunkDispatch } from 'redux-thunk';
|
|
import { GlobalTimeLoading, UpdateTimeInterval } from 'store/actions';
|
|
import { AppState } from 'store/reducers';
|
|
import AppActions from 'types/actions';
|
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
|
|
import CustomDateTimeModal, { DateTimeRangeType } from '../CustomDateTimeModal';
|
|
import RefreshText from './Refresh';
|
|
|
|
const DateTimeSelection = ({
|
|
location,
|
|
updateTimeInterval,
|
|
globalTimeLoading,
|
|
}: Props): JSX.Element => {
|
|
const [form_dtselector] = Form.useForm();
|
|
|
|
const params = new URLSearchParams(location.search);
|
|
const searchStartTime = params.get('startTime');
|
|
const searchEndTime = params.get('endTime');
|
|
|
|
const localstorageStartTime = getLocalStorageKey('startTime');
|
|
const localstorageEndTime = getLocalStorageKey('endTime');
|
|
|
|
const getTime = useCallback((): [number, number] | undefined => {
|
|
if (searchEndTime && searchStartTime) {
|
|
const startDate = dayjs(
|
|
new Date(parseInt(getTimeString(searchStartTime), 10)),
|
|
);
|
|
const endDate = dayjs(new Date(parseInt(getTimeString(searchEndTime), 10)));
|
|
|
|
return [startDate.toDate().getTime() || 0, endDate.toDate().getTime() || 0];
|
|
}
|
|
if (localstorageStartTime && localstorageEndTime) {
|
|
const startDate = dayjs(localstorageStartTime);
|
|
const endDate = dayjs(localstorageEndTime);
|
|
|
|
return [startDate.toDate().getTime() || 0, endDate.toDate().getTime() || 0];
|
|
}
|
|
return undefined;
|
|
}, [
|
|
localstorageEndTime,
|
|
localstorageStartTime,
|
|
searchEndTime,
|
|
searchStartTime,
|
|
]);
|
|
|
|
const [startTime, setStartTime] = useState<Dayjs>();
|
|
const [endTime, setEndTime] = useState<Dayjs>();
|
|
|
|
const [options, setOptions] = useState(getOptions(location.pathname));
|
|
const [refreshButtonHidden, setRefreshButtonHidden] = useState<boolean>(false);
|
|
const [customDateTimeVisible, setCustomDTPickerVisible] = useState<boolean>(
|
|
false,
|
|
);
|
|
|
|
const { maxTime, minTime, selectedTime } = useSelector<
|
|
AppState,
|
|
GlobalReducer
|
|
>((state) => state.globalTime);
|
|
|
|
const getDefaultTime = (pathName: string): Time => {
|
|
const defaultSelectedOption = getDefaultOption(pathName);
|
|
|
|
const routes = getLocalStorageKey(LOCAL_STORAGE.METRICS_TIME_IN_DURATION);
|
|
|
|
if (routes !== null) {
|
|
const routesObject = JSON.parse(routes || '{}');
|
|
const selectedTime = routesObject[pathName];
|
|
|
|
if (selectedTime) {
|
|
return selectedTime;
|
|
}
|
|
}
|
|
|
|
return defaultSelectedOption;
|
|
};
|
|
|
|
const [selectedTimeInterval, setSelectedTimeInterval] = useState<Time>(
|
|
getDefaultTime(location.pathname),
|
|
);
|
|
|
|
const updateLocalStorageForRoutes = (value: Time): void => {
|
|
const preRoutes = getLocalStorageKey(LOCAL_STORAGE.METRICS_TIME_IN_DURATION);
|
|
if (preRoutes !== null) {
|
|
const preRoutesObject = JSON.parse(preRoutes);
|
|
|
|
const preRoute = {
|
|
...preRoutesObject,
|
|
};
|
|
preRoute[location.pathname] = value;
|
|
|
|
setLocalStorageKey(
|
|
LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
|
|
JSON.stringify(preRoute),
|
|
);
|
|
}
|
|
};
|
|
|
|
const onSelectHandler = (value: Time): void => {
|
|
if (value !== 'custom') {
|
|
updateTimeInterval(value);
|
|
const selectedLabel = getInputLabel(undefined, undefined, value);
|
|
setSelectedTimeInterval(selectedLabel as Time);
|
|
updateLocalStorageForRoutes(value);
|
|
} else {
|
|
setRefreshButtonHidden(true);
|
|
setCustomDTPickerVisible(true);
|
|
}
|
|
};
|
|
|
|
const onRefreshHandler = (): void => {
|
|
onSelectHandler(selectedTimeInterval);
|
|
onLastRefreshHandler();
|
|
};
|
|
|
|
const getInputLabel = (
|
|
startTime?: Dayjs,
|
|
endTime?: Dayjs,
|
|
timeInterval: Time = '15min',
|
|
): string | Time => {
|
|
if (startTime && endTime && timeInterval === 'custom') {
|
|
const format = 'YYYY/MM/DD HH:mm';
|
|
|
|
const startString = startTime.format(format);
|
|
const endString = endTime.format(format);
|
|
|
|
return `${startString} - ${endString}`;
|
|
}
|
|
|
|
return timeInterval;
|
|
};
|
|
|
|
const onLastRefreshHandler = useCallback(() => {
|
|
const currentTime = dayjs();
|
|
|
|
const lastRefresh = dayjs(
|
|
selectedTimeInterval === 'custom' ? minTime / 1000000 : maxTime / 1000000,
|
|
);
|
|
|
|
const secondsDiff = currentTime.diff(lastRefresh, 'seconds');
|
|
|
|
const minutedDiff = currentTime.diff(lastRefresh, 'minutes');
|
|
const hoursDiff = currentTime.diff(lastRefresh, 'hours');
|
|
const daysDiff = currentTime.diff(lastRefresh, 'days');
|
|
const monthsDiff = currentTime.diff(lastRefresh, 'months');
|
|
|
|
if (monthsDiff > 0) {
|
|
return `Last refresh -${monthsDiff} months ago`;
|
|
}
|
|
|
|
if (daysDiff > 0) {
|
|
return `Last refresh - ${daysDiff} days ago`;
|
|
}
|
|
|
|
if (hoursDiff > 0) {
|
|
return `Last refresh - ${hoursDiff} hrs ago`;
|
|
}
|
|
|
|
if (minutedDiff > 0) {
|
|
return `Last refresh - ${minutedDiff} mins ago`;
|
|
}
|
|
|
|
return `Last refresh - ${secondsDiff} sec ago`;
|
|
}, [maxTime, minTime, selectedTimeInterval]);
|
|
|
|
const onCustomDateHandler = (dateTimeRange: DateTimeRangeType): void => {
|
|
if (dateTimeRange !== null) {
|
|
const [startTimeMoment, endTimeMoment] = dateTimeRange;
|
|
if (startTimeMoment && endTimeMoment) {
|
|
setSelectedTimeInterval('custom');
|
|
setStartTime(startTimeMoment);
|
|
setEndTime(endTimeMoment);
|
|
setCustomDTPickerVisible(false);
|
|
updateTimeInterval('custom', [
|
|
startTimeMoment?.toDate().getTime() || 0,
|
|
endTimeMoment?.toDate().getTime() || 0,
|
|
]);
|
|
setLocalStorageKey('startTime', startTimeMoment.toString());
|
|
setLocalStorageKey('endTime', endTimeMoment.toString());
|
|
updateLocalStorageForRoutes('custom');
|
|
}
|
|
}
|
|
};
|
|
|
|
// this is triggred when we change the routes and based on that we are changing the default options
|
|
useEffect(() => {
|
|
const metricsTimeDuration = getLocalStorageKey(
|
|
LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
|
|
);
|
|
|
|
if (metricsTimeDuration === null) {
|
|
setLocalStorageKey(
|
|
LOCAL_STORAGE.METRICS_TIME_IN_DURATION,
|
|
JSON.stringify({}),
|
|
);
|
|
}
|
|
|
|
const currentRoute = location.pathname;
|
|
const time = getDefaultTime(currentRoute);
|
|
|
|
const currentOptions = getOptions(currentRoute);
|
|
setOptions(currentOptions);
|
|
|
|
const getCustomOrIntervalTime = (time: Time): Time => {
|
|
if (searchEndTime !== null && searchStartTime !== null) {
|
|
return 'custom';
|
|
}
|
|
|
|
if (
|
|
(localstorageEndTime === null || localstorageStartTime === null) &&
|
|
time === 'custom'
|
|
) {
|
|
return getDefaultOption(currentRoute);
|
|
}
|
|
|
|
return time;
|
|
};
|
|
|
|
const updatedTime = getCustomOrIntervalTime(time);
|
|
|
|
const [preStartTime = 0, preEndTime = 0] = getTime() || [];
|
|
|
|
setStartTime(dayjs(preStartTime));
|
|
setEndTime(dayjs(preEndTime));
|
|
|
|
updateTimeInterval(updatedTime, [preStartTime, preEndTime]);
|
|
}, [
|
|
location.pathname,
|
|
getTime,
|
|
localstorageEndTime,
|
|
localstorageStartTime,
|
|
searchEndTime,
|
|
searchStartTime,
|
|
updateTimeInterval,
|
|
globalTimeLoading,
|
|
]);
|
|
|
|
return (
|
|
<Container>
|
|
<Form
|
|
form={form_dtselector}
|
|
layout="inline"
|
|
initialValues={{ interval: selectedTime }}
|
|
>
|
|
<DefaultSelect
|
|
onSelect={(value): void => onSelectHandler(value as Time)}
|
|
value={getInputLabel(startTime, endTime, selectedTime)}
|
|
data-testid="dropDown"
|
|
>
|
|
{options.map(({ value, label }) => (
|
|
<Option key={value + label} value={value}>
|
|
{label}
|
|
</Option>
|
|
))}
|
|
</DefaultSelect>
|
|
|
|
<FormItem hidden={refreshButtonHidden}>
|
|
<Button type="primary" onClick={onRefreshHandler}>
|
|
Refresh
|
|
</Button>
|
|
</FormItem>
|
|
</Form>
|
|
|
|
<RefreshText
|
|
{...{
|
|
onLastRefreshHandler,
|
|
}}
|
|
/>
|
|
|
|
<CustomDateTimeModal
|
|
visible={customDateTimeVisible}
|
|
onCreate={onCustomDateHandler}
|
|
onCancel={(): void => {
|
|
setCustomDTPickerVisible(false);
|
|
}}
|
|
/>
|
|
</Container>
|
|
);
|
|
};
|
|
|
|
interface DispatchProps {
|
|
updateTimeInterval: (
|
|
interval: Time,
|
|
dateTimeRange?: [number, number],
|
|
) => (dispatch: Dispatch<AppActions>) => void;
|
|
globalTimeLoading: () => void;
|
|
}
|
|
|
|
const mapDispatchToProps = (
|
|
dispatch: ThunkDispatch<unknown, unknown, AppActions>,
|
|
): DispatchProps => ({
|
|
updateTimeInterval: bindActionCreators(UpdateTimeInterval, dispatch),
|
|
globalTimeLoading: bindActionCreators(GlobalTimeLoading, dispatch),
|
|
});
|
|
|
|
type Props = DispatchProps & RouteComponentProps;
|
|
|
|
export default connect(null, mapDispatchToProps)(withRouter(DateTimeSelection));
|