mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-24 05:34:26 +08:00
feat: react is updated to v18 (#1948)
* feat: v5 is in progress * feat: antdv5 is updated * fix: build is fixed * fix: default config is over written by custom one * chore: onchange handler is updated * chore: overflow is hidden in the layout * feat: react is updated from v17 to v18 * feat: antdv5 is updated (#1880) * feat: v5 is in progress * feat: antdv5 is updated * fix: build is fixed * fix: default config is over written by custom one * chore: onchange handler is updated * chore: overflow is hidden in the layout * Update index.tsx * fix: import is fixed * chore: un used import is fixed * fix: dark mode is updated in service map * fix: config dropdown is updated * fix: logs types is updated * fix: copy clipboard notification is updated Co-authored-by: Pranay Prateek <pranay@signoz.io> * chore: all channel is updated move from usefetch to usequery * fix: typescript is fixed Co-authored-by: Pranay Prateek <pranay@signoz.io> Co-authored-by: Ankit Nayan <ankit@signoz.io>
This commit is contained in:
parent
24ac062bf5
commit
1c7202b5bf
@ -70,8 +70,8 @@
|
||||
"less-loader": "^10.2.0",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mini-css-extract-plugin": "2.4.5",
|
||||
"react": "17.0.0",
|
||||
"react-dom": "17.0.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-force-graph": "^1.41.0",
|
||||
"react-graph-vis": "^1.0.5",
|
||||
"react-grid-layout": "^1.3.4",
|
||||
@ -132,8 +132,8 @@
|
||||
"@types/lodash-es": "^4.17.4",
|
||||
"@types/mini-css-extract-plugin": "^2.5.1",
|
||||
"@types/node": "^16.10.3",
|
||||
"@types/react": "^17.0.0",
|
||||
"@types/react-dom": "^16.9.9",
|
||||
"@types/react": "18.0.0",
|
||||
"@types/react-dom": "18.0.0",
|
||||
"@types/react-grid-layout": "^1.1.2",
|
||||
"@types/react-redux": "^7.1.11",
|
||||
"@types/react-router-dom": "^5.1.6",
|
||||
@ -186,7 +186,7 @@
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "17.0.0",
|
||||
"@types/react-dom": "17.0.0"
|
||||
"@types/react": "18.0.0",
|
||||
"@types/react-dom": "18.0.0"
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
import AddToQueryHOC from '../AddToQueryHOC';
|
||||
import CopyClipboardHOC from '../CopyClipboardHOC';
|
||||
import { Container, Text, TextContainer } from './styles';
|
||||
import { Container, LogFieldContainer, Text, TextContainer } from './styles';
|
||||
import { isValidLogField } from './util';
|
||||
|
||||
interface LogFieldProps {
|
||||
@ -95,22 +95,24 @@ function LogItem({ logData }: LogItemProps): JSX.Element {
|
||||
<div style={{ maxWidth: '100%' }}>
|
||||
<div>
|
||||
{'{'}
|
||||
<div style={{ marginLeft: '0.5rem' }}>
|
||||
<LogGeneralField
|
||||
fieldKey="log"
|
||||
fieldValue={flattenLogData.body as never}
|
||||
/>
|
||||
{flattenLogData.stream && (
|
||||
<LogFieldContainer>
|
||||
<>
|
||||
<LogGeneralField
|
||||
fieldKey="stream"
|
||||
fieldValue={flattenLogData.stream as never}
|
||||
fieldKey="log"
|
||||
fieldValue={flattenLogData.body as never}
|
||||
/>
|
||||
)}
|
||||
<LogGeneralField
|
||||
fieldKey="timestamp"
|
||||
fieldValue={dayjs((flattenLogData.timestamp as never) / 1e6).format()}
|
||||
/>
|
||||
</div>
|
||||
{flattenLogData.stream && (
|
||||
<LogGeneralField
|
||||
fieldKey="stream"
|
||||
fieldValue={flattenLogData.stream as never}
|
||||
/>
|
||||
)}
|
||||
<LogGeneralField
|
||||
fieldKey="timestamp"
|
||||
fieldValue={dayjs((flattenLogData.timestamp as never) / 1e6).format()}
|
||||
/>
|
||||
</>
|
||||
</LogFieldContainer>
|
||||
{'}'}
|
||||
</div>
|
||||
<div>
|
||||
|
@ -29,3 +29,7 @@ export const TextContainer = styled.div`
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const LogFieldContainer = styled.div`
|
||||
margin-left: 0.5rem;
|
||||
`;
|
||||
|
@ -5,10 +5,10 @@ import Spinner from 'components/Spinner';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import ROUTES from 'constants/routes';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import useFetch from 'hooks/useFetch';
|
||||
import history from 'lib/history';
|
||||
import React, { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
@ -29,13 +29,13 @@ function AlertChannels(): JSX.Element {
|
||||
history.push(ROUTES.CHANNELS_NEW);
|
||||
}, []);
|
||||
|
||||
const { loading, payload, error, errorMessage } = useFetch(getAll);
|
||||
const { isLoading, data, isError } = useQuery('channels', getAll);
|
||||
|
||||
if (error) {
|
||||
return <Typography>{errorMessage}</Typography>;
|
||||
if (isError || data?.error) {
|
||||
return <Typography>{data?.error || 'Something went wrong'}</Typography>;
|
||||
}
|
||||
|
||||
if (loading || payload === undefined) {
|
||||
if (isLoading || data?.payload === undefined || data.payload === null) {
|
||||
return <Spinner tip={t('loading_channels_message')} height="90vh" />;
|
||||
}
|
||||
|
||||
@ -60,7 +60,7 @@ function AlertChannels(): JSX.Element {
|
||||
</RightActionContainer>
|
||||
</ButtonContainer>
|
||||
|
||||
<AlertChannelsComponent allChannels={payload} />
|
||||
<AlertChannelsComponent allChannels={data.payload} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -10,7 +10,8 @@ import {
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { ColumnType } from 'antd/es/table';
|
||||
import { ColumnType, TablePaginationConfig } from 'antd/es/table';
|
||||
import { FilterValue, SorterResult } from 'antd/es/table/interface';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { FilterConfirmProps } from 'antd/lib/table/interface';
|
||||
import getAll from 'api/errors/getAll';
|
||||
@ -30,6 +31,7 @@ import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { Exception, PayloadProps } from 'types/api/errors/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import { FilterDropdownExtendsProps } from './types';
|
||||
import {
|
||||
extractFilterValues,
|
||||
getDefaultFilterValue,
|
||||
@ -176,7 +178,13 @@ function AllErrors(): JSX.Element {
|
||||
);
|
||||
|
||||
const filterDropdownWrapper = useCallback(
|
||||
({ setSelectedKeys, selectedKeys, confirm, placeholder, filterKey }) => {
|
||||
({
|
||||
setSelectedKeys,
|
||||
selectedKeys,
|
||||
confirm,
|
||||
placeholder,
|
||||
filterKey,
|
||||
}: FilterDropdownExtendsProps) => {
|
||||
return (
|
||||
<Card size="small">
|
||||
<Space align="start" direction="vertical">
|
||||
@ -192,11 +200,11 @@ function AllErrors(): JSX.Element {
|
||||
getUpdatedServiceName,
|
||||
getUpdatedExceptionType,
|
||||
)}
|
||||
onPressEnter={handleSearch(confirm, selectedKeys[0], filterKey)}
|
||||
onPressEnter={handleSearch(confirm, String(selectedKeys[0]), filterKey)}
|
||||
/>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSearch(confirm, selectedKeys[0], filterKey)}
|
||||
onClick={handleSearch(confirm, String(selectedKeys[0]), filterKey)}
|
||||
icon={<SearchOutlined />}
|
||||
size="small"
|
||||
>
|
||||
@ -209,8 +217,8 @@ function AllErrors(): JSX.Element {
|
||||
[getUpdatedExceptionType, getUpdatedServiceName, handleSearch],
|
||||
);
|
||||
|
||||
const onExceptionTypeFilter = useCallback(
|
||||
(value, record: Exception): boolean => {
|
||||
const onExceptionTypeFilter: ColumnType<Exception>['onFilter'] = useCallback(
|
||||
(value: unknown, record: Exception): boolean => {
|
||||
if (record.exceptionType && typeof value === 'string') {
|
||||
return record.exceptionType.toLowerCase().includes(value.toLowerCase());
|
||||
}
|
||||
@ -220,7 +228,7 @@ function AllErrors(): JSX.Element {
|
||||
);
|
||||
|
||||
const onApplicationTypeFilter = useCallback(
|
||||
(value, record: Exception): boolean => {
|
||||
(value: unknown, record: Exception): boolean => {
|
||||
if (record.serviceName && typeof value === 'string') {
|
||||
return record.serviceName.toLowerCase().includes(value.toLowerCase());
|
||||
}
|
||||
@ -343,7 +351,11 @@ function AllErrors(): JSX.Element {
|
||||
];
|
||||
|
||||
const onChangeHandler: TableProps<Exception>['onChange'] = useCallback(
|
||||
(paginations, filters, sorter) => {
|
||||
(
|
||||
paginations: TablePaginationConfig,
|
||||
filters: Record<string, FilterValue | null>,
|
||||
sorter: SorterResult<Exception>[] | SorterResult<Exception>,
|
||||
) => {
|
||||
if (!Array.isArray(sorter)) {
|
||||
const { pageSize = 0, current = 0 } = paginations;
|
||||
const { columnKey = '', order } = sorter;
|
||||
|
9
frontend/src/container/AllError/types.ts
Normal file
9
frontend/src/container/AllError/types.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { FilterDropdownProps } from 'antd/es/table/interface';
|
||||
|
||||
export interface FilterDropdownExtendsProps {
|
||||
placeholder: string;
|
||||
filterKey: string;
|
||||
confirm: FilterDropdownProps['confirm'];
|
||||
setSelectedKeys: FilterDropdownProps['setSelectedKeys'];
|
||||
selectedKeys: FilterDropdownProps['selectedKeys'];
|
||||
}
|
@ -591,18 +591,20 @@ function GeneralSettings({
|
||||
|
||||
return (
|
||||
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
|
||||
{Element}
|
||||
<ErrorTextContainer>
|
||||
<TextToolTip
|
||||
{...{
|
||||
text: `More details on how to set retention period`,
|
||||
url: 'https://signoz.io/docs/userguide/retention-period/',
|
||||
}}
|
||||
/>
|
||||
{errorText && <ErrorText>{errorText}</ErrorText>}
|
||||
</ErrorTextContainer>
|
||||
<>
|
||||
{Element}
|
||||
<ErrorTextContainer>
|
||||
<TextToolTip
|
||||
{...{
|
||||
text: `More details on how to set retention period`,
|
||||
url: 'https://signoz.io/docs/userguide/retention-period/',
|
||||
}}
|
||||
/>
|
||||
{errorText && <ErrorText>{errorText}</ErrorText>}
|
||||
</ErrorTextContainer>
|
||||
|
||||
<Row justify="start">{renderConfig}</Row>
|
||||
<Row justify="start">{renderConfig}</Row>
|
||||
</>
|
||||
</Col>
|
||||
);
|
||||
}
|
||||
|
@ -68,7 +68,7 @@ function SearchFilter({
|
||||
);
|
||||
|
||||
const handleSearch = useCallback(
|
||||
(customQuery) => {
|
||||
(customQuery: string) => {
|
||||
if (liveTail === 'PLAYING') {
|
||||
dispatch({
|
||||
type: TOGGLE_LIVE_TAIL,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import history from 'lib/history';
|
||||
import { parseQuery, reverseParser } from 'lib/logql';
|
||||
import { ILogQLParsedQueryItem } from 'lib/logql/types';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
@ -10,19 +11,21 @@ import {
|
||||
} from 'types/actions/logs';
|
||||
import { ILogsReducer } from 'types/reducer/logs';
|
||||
|
||||
export function useSearchParser(): {
|
||||
export interface IUseSearchParser {
|
||||
queryString: string;
|
||||
parsedQuery: unknown;
|
||||
updateParsedQuery: (arg0: unknown) => void;
|
||||
updateQueryString: (arg0: unknown) => void;
|
||||
} {
|
||||
updateParsedQuery: (updatedParsedPayload: ILogQLParsedQueryItem[]) => void;
|
||||
updateQueryString: (updatedQueryString: string) => void;
|
||||
}
|
||||
|
||||
export function useSearchParser(): IUseSearchParser {
|
||||
const dispatch = useDispatch();
|
||||
const {
|
||||
searchFilter: { parsedQuery, queryString },
|
||||
} = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||
|
||||
const updateQueryString = useCallback(
|
||||
(updatedQueryString) => {
|
||||
(updatedQueryString: string) => {
|
||||
history.replace({
|
||||
pathname: history.location.pathname,
|
||||
search: updatedQueryString ? `?q=${updatedQueryString}` : '',
|
||||
@ -48,7 +51,7 @@ export function useSearchParser(): {
|
||||
}, [queryString, updateQueryString]);
|
||||
|
||||
const updateParsedQuery = useCallback(
|
||||
(updatedParsedPayload) => {
|
||||
(updatedParsedPayload: ILogQLParsedQueryItem[]) => {
|
||||
dispatch({
|
||||
type: SET_SEARCH_QUERY_PARSED_PAYLOAD,
|
||||
payload: updatedParsedPayload,
|
||||
|
@ -2,12 +2,15 @@ import { blue } from '@ant-design/colors';
|
||||
import { SearchOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Input, Space, Table } from 'antd';
|
||||
import type { ColumnsType, ColumnType } from 'antd/es/table';
|
||||
import type { FilterConfirmProps } from 'antd/es/table/interface';
|
||||
import type {
|
||||
FilterConfirmProps,
|
||||
FilterDropdownProps,
|
||||
} from 'antd/es/table/interface';
|
||||
import localStorageGet from 'api/browser/localstorage/get';
|
||||
import localStorageSet from 'api/browser/localstorage/set';
|
||||
import { SKIP_ONBOARDING } from 'constants/onboarding';
|
||||
import ROUTES from 'constants/routes';
|
||||
import React, { useCallback, useState } from 'react';
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { AppState } from 'store/reducers';
|
||||
@ -35,8 +38,8 @@ function Metrics(): JSX.Element {
|
||||
confirm();
|
||||
};
|
||||
|
||||
const FilterIcon = useCallback(
|
||||
({ filtered }) => (
|
||||
const FilterIcon: ColumnType<DataProps>['filterIcon'] = useCallback(
|
||||
(filtered: boolean) => (
|
||||
<SearchOutlined
|
||||
style={{
|
||||
color: filtered ? blue[6] : undefined,
|
||||
@ -47,7 +50,7 @@ function Metrics(): JSX.Element {
|
||||
);
|
||||
|
||||
const filterDropdown = useCallback(
|
||||
({ setSelectedKeys, selectedKeys, confirm }) => (
|
||||
({ setSelectedKeys, selectedKeys, confirm }: FilterDropdownProps) => (
|
||||
<Card size="small">
|
||||
<Space align="start" direction="vertical">
|
||||
<Input
|
||||
@ -73,6 +76,60 @@ function Metrics(): JSX.Element {
|
||||
[],
|
||||
);
|
||||
|
||||
type DataIndex = keyof ServicesList;
|
||||
|
||||
const getColumnSearchProps = useCallback(
|
||||
(dataIndex: DataIndex): ColumnType<DataProps> => ({
|
||||
filterDropdown,
|
||||
filterIcon: FilterIcon,
|
||||
onFilter: (value: string | number | boolean, record: DataProps): boolean =>
|
||||
record[dataIndex]
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(value.toString().toLowerCase()),
|
||||
render: (text: string): JSX.Element => (
|
||||
<Link to={`${ROUTES.APPLICATION}/${text}${search}`}>
|
||||
<Name>{text}</Name>
|
||||
</Link>
|
||||
),
|
||||
}),
|
||||
[filterDropdown, FilterIcon, search],
|
||||
);
|
||||
|
||||
const columns: ColumnsType<DataProps> = useMemo(
|
||||
() => [
|
||||
{
|
||||
title: 'Application',
|
||||
dataIndex: 'serviceName',
|
||||
key: 'serviceName',
|
||||
...getColumnSearchProps('serviceName'),
|
||||
},
|
||||
{
|
||||
title: 'P99 latency (in ms)',
|
||||
dataIndex: 'p99',
|
||||
key: 'p99',
|
||||
defaultSortOrder: 'descend',
|
||||
sorter: (a: DataProps, b: DataProps): number => a.p99 - b.p99,
|
||||
render: (value: number): string => (value / 1000000).toFixed(2),
|
||||
},
|
||||
{
|
||||
title: 'Error Rate (% of total)',
|
||||
dataIndex: 'errorRate',
|
||||
key: 'errorRate',
|
||||
sorter: (a: DataProps, b: DataProps): number => a.errorRate - b.errorRate,
|
||||
render: (value: number): string => value.toFixed(2),
|
||||
},
|
||||
{
|
||||
title: 'Operations Per Second',
|
||||
dataIndex: 'callRate',
|
||||
key: 'callRate',
|
||||
sorter: (a: DataProps, b: DataProps): number => a.callRate - b.callRate,
|
||||
render: (value: number): string => value.toFixed(2),
|
||||
},
|
||||
],
|
||||
[getColumnSearchProps],
|
||||
);
|
||||
|
||||
if (
|
||||
services.length === 0 &&
|
||||
loading === false &&
|
||||
@ -82,56 +139,6 @@ function Metrics(): JSX.Element {
|
||||
return <SkipBoardModal onContinueClick={onContinueClick} />;
|
||||
}
|
||||
|
||||
type DataIndex = keyof ServicesList;
|
||||
|
||||
const getColumnSearchProps = (
|
||||
dataIndex: DataIndex,
|
||||
): ColumnType<DataProps> => ({
|
||||
filterDropdown,
|
||||
filterIcon: FilterIcon,
|
||||
onFilter: (value: string | number | boolean, record: DataProps): boolean =>
|
||||
record[dataIndex]
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.includes(value.toString().toLowerCase()),
|
||||
render: (text: string): JSX.Element => (
|
||||
<Link to={`${ROUTES.APPLICATION}/${text}${search}`}>
|
||||
<Name>{text}</Name>
|
||||
</Link>
|
||||
),
|
||||
});
|
||||
|
||||
const columns: ColumnsType<DataProps> = [
|
||||
{
|
||||
title: 'Application',
|
||||
dataIndex: 'serviceName',
|
||||
key: 'serviceName',
|
||||
...getColumnSearchProps('serviceName'),
|
||||
},
|
||||
{
|
||||
title: 'P99 latency (in ms)',
|
||||
dataIndex: 'p99',
|
||||
key: 'p99',
|
||||
defaultSortOrder: 'descend',
|
||||
sorter: (a: DataProps, b: DataProps): number => a.p99 - b.p99,
|
||||
render: (value: number): string => (value / 1000000).toFixed(2),
|
||||
},
|
||||
{
|
||||
title: 'Error Rate (% of total)',
|
||||
dataIndex: 'errorRate',
|
||||
key: 'errorRate',
|
||||
sorter: (a: DataProps, b: DataProps): number => a.errorRate - b.errorRate,
|
||||
render: (value: number): string => value.toFixed(2),
|
||||
},
|
||||
{
|
||||
title: 'Operations Per Second',
|
||||
dataIndex: 'callRate',
|
||||
key: 'callRate',
|
||||
sorter: (a: DataProps, b: DataProps): number => a.callRate - b.callRate,
|
||||
render: (value: number): string => value.toFixed(2),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Table
|
||||
|
@ -51,9 +51,12 @@ function EditMembersDetails({
|
||||
}
|
||||
}, [state.error, state.value, t]);
|
||||
|
||||
const onPasswordChangeHandler = useCallback((event) => {
|
||||
setPasswordLink(event.target.value);
|
||||
}, []);
|
||||
const onPasswordChangeHandler: React.ChangeEventHandler<HTMLInputElement> = useCallback(
|
||||
(event) => {
|
||||
setPasswordLink(event.target.value);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const onGeneratePasswordHandler = async (): Promise<void> => {
|
||||
try {
|
||||
|
@ -171,7 +171,7 @@ function Duration(): JSX.Element {
|
||||
updatedUrl(min, max);
|
||||
};
|
||||
|
||||
const TipComponent = useCallback((value) => {
|
||||
const TipComponent = useCallback((value: number | undefined) => {
|
||||
if (value === undefined) {
|
||||
return <div />;
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import './ReactI18';
|
||||
import AppRoutes from 'AppRoutes';
|
||||
import { ThemeProvider } from 'hooks/useDarkMode';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { ReactQueryDevtools } from 'react-query/devtools';
|
||||
import { Provider } from 'react-redux';
|
||||
@ -23,18 +23,23 @@ const queryClient = new QueryClient({
|
||||
},
|
||||
});
|
||||
|
||||
ReactDOM.render(
|
||||
<ThemeProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Provider store={store}>
|
||||
<React.StrictMode>
|
||||
<AppRoutes />
|
||||
</React.StrictMode>
|
||||
</Provider>
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
)}
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>,
|
||||
document.querySelector('#root'),
|
||||
);
|
||||
const container = document.getElementById('root');
|
||||
|
||||
if (container) {
|
||||
const root = createRoot(container);
|
||||
|
||||
root.render(
|
||||
<ThemeProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Provider store={store}>
|
||||
<React.StrictMode>
|
||||
<AppRoutes />
|
||||
</React.StrictMode>
|
||||
</Provider>
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<ReactQueryDevtools initialIsOpen />
|
||||
)}
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>,
|
||||
);
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ function Trace({
|
||||
};
|
||||
}, [dispatch]);
|
||||
|
||||
const onClickHandler = useCallback(
|
||||
const onClickHandler: React.MouseEventHandler<HTMLElement> = useCallback(
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user