diff --git a/frontend/src/container/GeneralSettings/GeneralSettings.tsx b/frontend/src/container/GeneralSettings/GeneralSettings.tsx new file mode 100644 index 0000000000..768322d91d --- /dev/null +++ b/frontend/src/container/GeneralSettings/GeneralSettings.tsx @@ -0,0 +1,323 @@ +import { Button, Col, Modal, notification, Row, Typography } from 'antd'; +import setRetentionApi from 'api/settings/setRetention'; +import TextToolTip from 'components/TextToolTip'; +import find from 'lodash-es/find'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { + IDiskType, + PayloadProps as GetDisksPayload, +} from 'types/api/disks/getDisks'; +import { PayloadProps as GetRetentionPayload } from 'types/api/settings/getRetention'; + +import Retention from './Retention'; +import { ButtonContainer, ErrorText, ErrorTextContainer } from './styles'; + +type NumberOrNull = number | null; + +function GeneralSettings({ + ttlValuesPayload, + getAvailableDiskPayload, +}: GeneralSettingsProps): JSX.Element { + const { t } = useTranslation(); + const [modal, setModal] = useState(false); + const [postApiLoading, setPostApiLoading] = useState(false); + + const [availableDisks] = useState(getAvailableDiskPayload); + + const [currentTTLValues, setCurrentTTLValues] = useState(ttlValuesPayload); + + const [ + metricsTotalRetentionPeriod, + setMetricsTotalRetentionPeriod, + ] = useState(null); + const [ + metricsS3RetentionPeriod, + setMetricsS3RetentionPeriod, + ] = useState(null); + const [ + tracesTotalRetentionPeriod, + setTracesTotalRetentionPeriod, + ] = useState(null); + const [ + tracesS3RetentionPeriod, + setTracesS3RetentionPeriod, + ] = useState(null); + + useEffect(() => { + if (currentTTLValues) { + setMetricsTotalRetentionPeriod(currentTTLValues.metrics_ttl_duration_hrs); + setMetricsS3RetentionPeriod( + currentTTLValues.metrics_move_ttl_duration_hrs + ? currentTTLValues.metrics_move_ttl_duration_hrs + : null, + ); + setTracesTotalRetentionPeriod(currentTTLValues.traces_ttl_duration_hrs); + setTracesS3RetentionPeriod( + currentTTLValues.traces_move_ttl_duration_hrs + ? currentTTLValues.traces_move_ttl_duration_hrs + : null, + ); + } + }, [currentTTLValues]); + + const onModalToggleHandler = (): void => { + setModal((modal) => !modal); + }; + + const onClickSaveHandler = useCallback(() => { + onModalToggleHandler(); + }, []); + + const s3Enabled = useMemo( + () => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'), + [availableDisks], + ); + + const renderConfig = [ + { + name: 'Metrics', + retentionFields: [ + { + name: t('settings.total_retention_period'), + value: metricsTotalRetentionPeriod, + setValue: setMetricsTotalRetentionPeriod, + }, + { + name: t('settings.move_to_s3'), + value: metricsS3RetentionPeriod, + setValue: setMetricsS3RetentionPeriod, + hide: !s3Enabled, + }, + ], + }, + { + name: 'Traces', + retentionFields: [ + { + name: t('settings.total_retention_period'), + value: tracesTotalRetentionPeriod, + setValue: setTracesTotalRetentionPeriod, + }, + { + name: t('settings.move_to_s3'), + value: tracesS3RetentionPeriod, + setValue: setTracesS3RetentionPeriod, + hide: !s3Enabled, + }, + ], + }, + ].map((category): JSX.Element | null => { + if ( + Array.isArray(category.retentionFields) && + category.retentionFields.length > 0 + ) { + return ( + + {category.name} + + {category.retentionFields.map((retentionField) => ( + + ))} + + ); + } + return null; + }); + + // eslint-disable-next-line sonarjs/cognitive-complexity + const onOkHandler = async (): Promise => { + try { + setPostApiLoading(true); + const apiCalls = []; + + if ( + !( + currentTTLValues?.metrics_move_ttl_duration_hrs === + metricsS3RetentionPeriod && + currentTTLValues.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod + ) + ) { + apiCalls.push(() => + setRetentionApi({ + type: 'metrics', + totalDuration: `${metricsTotalRetentionPeriod || -1}h`, + coldStorage: s3Enabled ? 's3' : null, + toColdDuration: `${metricsS3RetentionPeriod || -1}h`, + }), + ); + } else { + apiCalls.push(() => Promise.resolve(null)); + } + + if ( + !( + currentTTLValues?.traces_move_ttl_duration_hrs === + tracesS3RetentionPeriod && + currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod + ) + ) { + apiCalls.push(() => + setRetentionApi({ + type: 'traces', + totalDuration: `${tracesTotalRetentionPeriod || -1}h`, + coldStorage: s3Enabled ? 's3' : null, + toColdDuration: `${tracesS3RetentionPeriod || -1}h`, + }), + ); + } else { + apiCalls.push(() => Promise.resolve(null)); + } + const apiCallSequence = ['metrics', 'traces']; + const apiResponses = await Promise.all(apiCalls.map((api) => api())); + + apiResponses.forEach((apiResponse, idx) => { + const name = apiCallSequence[idx]; + if (apiResponse) { + if (apiResponse.statusCode === 200) { + notification.success({ + message: 'Success!', + placement: 'topRight', + + description: t('settings.retention_success_message', { name }), + }); + } else { + notification.error({ + message: 'Error', + description: t('settings.retention_error_message', { name }), + placement: 'topRight', + }); + } + } + }); + onModalToggleHandler(); + setPostApiLoading(false); + } catch (error) { + notification.error({ + message: 'Error', + description: t('settings.retention_failed_message'), + placement: 'topRight', + }); + } + // Updates the currentTTL Values in order to avoid pushing the same values. + setCurrentTTLValues({ + metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1, + metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1, + traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1, + traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1, + }); + + setModal(false); + }; + + // eslint-disable-next-line sonarjs/cognitive-complexity + const [isDisabled, errorText] = useMemo((): [boolean, string] => { + // Various methods to return dynamic error message text. + const messages = { + compareError: (name: string | number): string => + t('settings.retention_comparison_error', { name }), + nullValueError: (name: string | number): string => + t('settings.retention_null_value_error', { name }), + }; + + // Defaults to button not disabled and empty error message text. + let isDisabled = false; + let errorText = ''; + + if (s3Enabled) { + if ( + (metricsTotalRetentionPeriod || metricsS3RetentionPeriod) && + Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod) + ) { + isDisabled = true; + errorText = messages.compareError('metrics'); + } else if ( + (tracesTotalRetentionPeriod || tracesS3RetentionPeriod) && + Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod) + ) { + isDisabled = true; + errorText = messages.compareError('traces'); + } + } + + if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) { + isDisabled = true; + if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) { + errorText = messages.nullValueError('metrics and traces'); + } else if (!metricsTotalRetentionPeriod) { + errorText = messages.nullValueError('metrics'); + } else if (!tracesTotalRetentionPeriod) { + errorText = messages.nullValueError('traces'); + } + } + if ( + currentTTLValues?.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod && + currentTTLValues.metrics_move_ttl_duration_hrs === + metricsS3RetentionPeriod && + currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod && + currentTTLValues.traces_move_ttl_duration_hrs === tracesS3RetentionPeriod + ) { + isDisabled = true; + } + return [isDisabled, errorText]; + }, [ + currentTTLValues, + metricsS3RetentionPeriod, + metricsTotalRetentionPeriod, + s3Enabled, + t, + tracesS3RetentionPeriod, + tracesTotalRetentionPeriod, + ]); + + return ( + + {Element} + + + {errorText && {errorText}} + + + {renderConfig} + + + {t('settings.retention_confirmation_description')} + + + + + + + ); +} + +interface GeneralSettingsProps { + ttlValuesPayload: GetRetentionPayload; + getAvailableDiskPayload: GetDisksPayload; +} + +export default GeneralSettings; diff --git a/frontend/src/container/GeneralSettings/index.tsx b/frontend/src/container/GeneralSettings/index.tsx index 6a76282192..633c4822e6 100644 --- a/frontend/src/container/GeneralSettings/index.tsx +++ b/frontend/src/container/GeneralSettings/index.tsx @@ -1,331 +1,52 @@ -/* eslint-disable sonarjs/cognitive-complexity */ -import { Button, Col, Modal, notification, Row, Typography } from 'antd'; +import { Typography } from 'antd'; import getDisks from 'api/disks/getDisks'; import getRetentionPeriodApi from 'api/settings/getRetention'; -import setRetentionApi from 'api/settings/setRetention'; import Spinner from 'components/Spinner'; -import TextToolTip from 'components/TextToolTip'; -import useFetch from 'hooks/useFetch'; -import { find } from 'lodash-es'; -import React, { useCallback, useEffect, useMemo, useState } from 'react'; +import React from 'react'; import { useTranslation } from 'react-i18next'; -import { IDiskType } from 'types/api/disks/getDisks'; -import { PayloadProps } from 'types/api/settings/getRetention'; +import { useQueries } from 'react-query'; -import Retention from './Retention'; -import { ButtonContainer, ErrorText, ErrorTextContainer } from './styles'; +import GeneralSettingsContainer from './GeneralSettings'; function GeneralSettings(): JSX.Element { - const { t } = useTranslation(); - const [notifications, Element] = notification.useNotification(); - const [modal, setModal] = useState(false); - const [postApiLoading, setPostApiLoading] = useState(false); - - const [availableDisks, setAvailableDisks] = useState(null); - - useEffect(() => { - getDisks().then((response) => setAvailableDisks(response.payload)); - }, []); - - const { payload, loading, error, errorMessage } = useFetch< - PayloadProps, - undefined - >(getRetentionPeriodApi, undefined); - - const [currentTTLValues, setCurrentTTLValues] = useState(payload); - - useEffect(() => { - setCurrentTTLValues(payload); - }, [payload]); - - const [metricsTotalRetentionPeriod, setMetricsTotalRetentionPeriod] = useState< - number | null - >(null); - const [metricsS3RetentionPeriod, setMetricsS3RetentionPeriod] = useState< - number | null - >(null); - const [tracesTotalRetentionPeriod, setTracesTotalRetentionPeriod] = useState< - number | null - >(null); - const [tracesS3RetentionPeriod, setTracesS3RetentionPeriod] = useState< - number | null - >(null); - - useEffect(() => { - if (currentTTLValues) { - setMetricsTotalRetentionPeriod(currentTTLValues.metrics_ttl_duration_hrs); - setMetricsS3RetentionPeriod( - currentTTLValues.metrics_move_ttl_duration_hrs - ? currentTTLValues.metrics_move_ttl_duration_hrs - : null, - ); - setTracesTotalRetentionPeriod(currentTTLValues.traces_ttl_duration_hrs); - setTracesS3RetentionPeriod( - currentTTLValues.traces_move_ttl_duration_hrs - ? currentTTLValues.traces_move_ttl_duration_hrs - : null, - ); - } - console.log({ changed: currentTTLValues }); - }, [currentTTLValues]); - - const onModalToggleHandler = (): void => { - setModal((modal) => !modal); - }; - - const onClickSaveHandler = useCallback(() => { - onModalToggleHandler(); - }, []); - - const s3Enabled = useMemo( - () => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'), - [availableDisks], - ); - - const renderConfig = [ + const { t } = useTranslation('common'); + const [getRetentionPeriodApiResponse, getDisksResponse] = useQueries([ { - name: 'Metrics', - retentionFields: [ - { - name: t('settings.total_retention_period'), - value: metricsTotalRetentionPeriod, - setValue: setMetricsTotalRetentionPeriod, - }, - { - name: t('settings.move_to_s3'), - value: metricsS3RetentionPeriod, - setValue: setMetricsS3RetentionPeriod, - hide: !s3Enabled, - }, - ], + queryFn: getRetentionPeriodApi, + queryKey: 'getRetentionPeriodApi', }, { - name: 'Traces', - retentionFields: [ - { - name: t('settings.total_retention_period'), - value: tracesTotalRetentionPeriod, - setValue: setTracesTotalRetentionPeriod, - }, - { - name: t('settings.move_to_s3'), - value: tracesS3RetentionPeriod, - setValue: setTracesS3RetentionPeriod, - hide: !s3Enabled, - }, - ], + queryFn: getDisks, + queryKey: 'getDisks', }, - ].map((category): JSX.Element | null => { - if ( - Array.isArray(category.retentionFields) && - category.retentionFields.length > 0 - ) { - return ( - - {category.name} - - {category.retentionFields.map((retentionField) => ( - - ))} - - ); - } - return null; - }); - - const onOkHandler = async (): Promise => { - try { - setPostApiLoading(true); - const apiCalls = []; - - if ( - !( - currentTTLValues?.metrics_move_ttl_duration_hrs === - metricsS3RetentionPeriod && - currentTTLValues.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod - ) - ) { - apiCalls.push(() => - setRetentionApi({ - type: 'metrics', - totalDuration: `${metricsTotalRetentionPeriod || -1}h`, - coldStorage: s3Enabled ? 's3' : null, - toColdDuration: `${metricsS3RetentionPeriod || -1}h`, - }), - ); - } else { - apiCalls.push(() => Promise.resolve(null)); - } - - if ( - !( - currentTTLValues?.traces_move_ttl_duration_hrs === - tracesS3RetentionPeriod && - currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod - ) - ) { - apiCalls.push(() => - setRetentionApi({ - type: 'traces', - totalDuration: `${tracesTotalRetentionPeriod || -1}h`, - coldStorage: s3Enabled ? 's3' : null, - toColdDuration: `${tracesS3RetentionPeriod || -1}h`, - }), - ); - } else { - apiCalls.push(() => Promise.resolve(null)); - } - const apiCallSequence = ['metrics', 'traces']; - const apiResponses = await Promise.all(apiCalls.map((api) => api())); - - apiResponses.forEach((apiResponse, idx) => { - const name = apiCallSequence[idx]; - if (apiResponse) { - if (apiResponse.statusCode === 200) { - notifications.success({ - message: 'Success!', - placement: 'topRight', - - description: t('settings.retention_success_message', { name }), - }); - } else { - notifications.error({ - message: 'Error', - description: t('settings.retention_error_message', { name }), - placement: 'topRight', - }); - } - } - }); - onModalToggleHandler(); - setPostApiLoading(false); - } catch (error) { - notifications.error({ - message: 'Error', - description: t('settings.retention_failed_message'), - placement: 'topRight', - }); - } - // Updates the currentTTL Values in order to avoid pushing the same values. - setCurrentTTLValues({ - metrics_ttl_duration_hrs: metricsTotalRetentionPeriod || -1, - metrics_move_ttl_duration_hrs: metricsS3RetentionPeriod || -1, - traces_ttl_duration_hrs: tracesTotalRetentionPeriod || -1, - traces_move_ttl_duration_hrs: tracesS3RetentionPeriod || -1, - }); - - setModal(false); - }; - - const [isDisabled, errorText] = useMemo((): [boolean, string] => { - // Various methods to return dynamic error message text. - const messages = { - compareError: (name: string | number): string => - t('settings.retention_comparison_error', { name }), - nullValueError: (name: string | number): string => - t('settings.retention_null_value_error', { name }), - }; - - // Defaults to button not disabled and empty error message text. - let isDisabled = false; - let errorText = ''; - - if (s3Enabled) { - if ( - (metricsTotalRetentionPeriod || metricsS3RetentionPeriod) && - Number(metricsTotalRetentionPeriod) <= Number(metricsS3RetentionPeriod) - ) { - isDisabled = true; - errorText = messages.compareError('metrics'); - } else if ( - (tracesTotalRetentionPeriod || tracesS3RetentionPeriod) && - Number(tracesTotalRetentionPeriod) <= Number(tracesS3RetentionPeriod) - ) { - isDisabled = true; - errorText = messages.compareError('traces'); - } - } - - if (!metricsTotalRetentionPeriod || !tracesTotalRetentionPeriod) { - isDisabled = true; - if (!metricsTotalRetentionPeriod && !tracesTotalRetentionPeriod) { - errorText = messages.nullValueError('metrics and traces'); - } else if (!metricsTotalRetentionPeriod) { - errorText = messages.nullValueError('metrics'); - } else if (!tracesTotalRetentionPeriod) { - errorText = messages.nullValueError('traces'); - } - } - if ( - currentTTLValues?.metrics_ttl_duration_hrs === metricsTotalRetentionPeriod && - currentTTLValues.metrics_move_ttl_duration_hrs === - metricsS3RetentionPeriod && - currentTTLValues.traces_ttl_duration_hrs === tracesTotalRetentionPeriod && - currentTTLValues.traces_move_ttl_duration_hrs === tracesS3RetentionPeriod - ) { - isDisabled = true; - } - return [isDisabled, errorText]; - }, [ - currentTTLValues, - metricsS3RetentionPeriod, - metricsTotalRetentionPeriod, - s3Enabled, - t, - tracesS3RetentionPeriod, - tracesTotalRetentionPeriod, ]); - if (error) { - return {errorMessage}; + if (getRetentionPeriodApiResponse.isError || getDisksResponse.isError) { + return ( + + {getRetentionPeriodApiResponse.data?.error || + getDisksResponse.data?.error || + t('something_went_wrong')} + + ); } - if (loading || currentTTLValues === undefined) { + if ( + getRetentionPeriodApiResponse.isLoading || + getDisksResponse.isLoading || + !getDisksResponse.data?.payload || + !getRetentionPeriodApiResponse.data?.payload + ) { return ; } return ( - - {Element} - - - {errorText && {errorText}} - - - {renderConfig} - - - {t('settings.retention_confirmation_description')} - - - - - - + ); } diff --git a/frontend/src/container/ListAlertRules/index.tsx b/frontend/src/container/ListAlertRules/index.tsx index ce44e871e9..8fd131736c 100644 --- a/frontend/src/container/ListAlertRules/index.tsx +++ b/frontend/src/container/ListAlertRules/index.tsx @@ -1,29 +1,29 @@ import getAll from 'api/alerts/getAll'; import Spinner from 'components/Spinner'; -import useFetch from 'hooks/useFetch'; import React from 'react'; -import { PayloadProps } from 'types/api/alerts/getAll'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; import ListAlert from './ListAlert'; function ListAlertRules(): JSX.Element { - const { loading, payload, error, errorMessage } = useFetch< - PayloadProps, - undefined - >(getAll); + const { t } = useTranslation('common'); + const { data, isError, isLoading } = useQuery('allAlerts', { + queryFn: getAll, + }); - if (error) { - return
{errorMessage}
; + if (isError) { + return
{data?.error || t('something_went_wrong')}
; } - if (loading || payload === undefined) { + if (isLoading || !data?.payload) { return ; } return ( ); diff --git a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx index 1d90703246..756bb54225 100644 --- a/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx +++ b/frontend/src/container/Trace/Search/AllTags/Tag/TagValue.tsx @@ -1,10 +1,9 @@ import { Select } from 'antd'; import getTagValue from 'api/trace/getTagValue'; -import useFetch from 'hooks/useFetch'; import React from 'react'; +import { useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; -import { PayloadProps, Props } from 'types/api/trace/getTagValue'; import { GlobalReducer } from 'types/reducer/globalTime'; import { TraceReducer } from 'types/reducer/trace'; @@ -22,11 +21,17 @@ function TagValue(props: TagValueProps): JSX.Element { (state) => state.globalTime, ); - const valueSuggestion = useFetch(getTagValue, { - end: globalReducer.maxTime, - start: globalReducer.minTime, - tagKey, - }); + const { isLoading, data } = useQuery( + ['tagKey', globalReducer.minTime, globalReducer.maxTime, tagKey], + { + queryFn: () => + getTagValue({ + end: globalReducer.maxTime, + start: globalReducer.minTime, + tagKey, + }), + }, + ); return ( - {valueSuggestion.payload && - valueSuggestion.payload.map((suggestion) => ( + {data && + data.payload && + data.payload.map((suggestion) => ( {suggestion.tagValues} diff --git a/frontend/src/pages/ChannelsEdit/index.tsx b/frontend/src/pages/ChannelsEdit/index.tsx index 4048eda81c..dc2804da53 100644 --- a/frontend/src/pages/ChannelsEdit/index.tsx +++ b/frontend/src/pages/ChannelsEdit/index.tsx @@ -8,32 +8,33 @@ import { WebhookType, } from 'container/CreateAlertChannels/config'; import EditAlertChannels from 'container/EditAlertChannels'; -import useFetch from 'hooks/useFetch'; import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { useQuery } from 'react-query'; import { useParams } from 'react-router-dom'; -import { PayloadProps, Props } from 'types/api/channels/get'; function ChannelsEdit(): JSX.Element { const { id } = useParams(); + const { t } = useTranslation(); - const { errorMessage, payload, error, loading } = useFetch< - PayloadProps, - Props - >(get, { - id, + const { isLoading, isError, data } = useQuery(['getChannel', id], { + queryFn: () => + get({ + id, + }), }); - if (error) { - return {errorMessage}; + if (isError) { + return {data?.error || t('something_went_wrong')}; } - if (loading || payload === undefined) { + if (isLoading || !data?.payload) { return ; } - const { data } = payload; + const { data: ChannelData } = data.payload; - const value = JSON.parse(data); + const value = JSON.parse(ChannelData); let type = ''; let channel: SlackChannel & WebhookChannel = { name: '' }; @@ -57,7 +58,7 @@ function ChannelsEdit(): JSX.Element { } type = WebhookType; } - console.log('channel:', channel); + return ( (); + const { t } = useTranslation('common'); - const { loading, error, payload, errorMessage } = useFetch< - PayloadProps, - Props - >(get, { - id: parseInt(ruleId, 10), + const { isLoading, data, isError } = useQuery(['ruleId', ruleId], { + queryFn: () => + get({ + id: parseInt(ruleId, 10), + }), }); - if (error) { - return
{errorMessage}
; + if (isError) { + return
{data?.error || t('something_went_wrong')}
; } - if (loading || payload === undefined) { + if (isLoading || !data?.payload) { return ; } - return ; + return ; } interface EditRulesParam {