Merge pull request #949 from pranshuchittora/pranshuchittora/feat/ttl-s3

feat(FE): TTL/s3 integration
This commit is contained in:
palash-signoz 2022-04-05 16:28:41 +05:30 committed by GitHub
commit 6e446dc0ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 408 additions and 255 deletions

View File

@ -1,3 +1,18 @@
{
"monitor_signup": "Monitor your applications. Find what is causing issues."
"monitor_signup": "Monitor your applications. Find what is causing issues.",
"routes": {
"general": "General",
"alert_channels": "Alert Channels"
},
"settings": {
"total_retention_period": "Total Retention Period",
"move_to_s3": "Move to S3\n(should be lower than total retention period)",
"retention_success_message": "Congrats. The retention periods for {{name}} has been updated successfully.",
"retention_error_message": "There was an issue in changing the retention period for {{name}}. Please try again or reach out to support@signoz.io",
"retention_failed_message": "There was an issue in changing the retention period. Please try again or reach out to support@signoz.io",
"retention_comparison_error": "Total retention period for {{name}} cant be lower or equal to the period after which data is moved to s3.",
"retention_null_value_error": "Retention Period for {{name}} is not set yet. Please set by choosing below",
"retention_confirmation": "Are you sure you want to change the retention period?",
"retention_confirmation_description": "This will change the amount of storage needed for saving metrics & traces."
}
}

View File

@ -0,0 +1,24 @@
import axios from 'api';
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
import { AxiosError } from 'axios';
import { ErrorResponse, SuccessResponse } from 'types/api';
import { PayloadProps } from 'types/api/disks/getDisks';
const getDisks = async (): Promise<
SuccessResponse<PayloadProps> | ErrorResponse
> => {
try {
const response = await axios.get(`/disks`);
return {
statusCode: 200,
error: null,
message: 'Success',
payload: response.data,
};
} catch (error) {
return ErrorResponseHandler(error as AxiosError);
}
};
export default getDisks;

View File

@ -9,7 +9,11 @@ const setRetention = async (
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
try {
const response = await axios.post<PayloadProps>(
`/settings/ttl?duration=${props.duration}&type=${props.type}`,
`/settings/ttl?duration=${props.totalDuration}&type=${props.type}${
props.coldStorage
? `&coldStorage=${props.coldStorage};toColdDuration=${props.toColdDuration}`
: ''
}`,
);
return {

View File

@ -1,108 +1,118 @@
import { DownOutlined } from '@ant-design/icons';
import { Button, Menu } from 'antd';
import React from 'react';
import { Col, Row, Select } from 'antd';
import { find } from 'lodash-es';
import React, { useEffect, useRef, useState } from 'react';
import { SettingPeroid } from '.';
import {
Dropdown,
Input,
RetentionContainer,
TextContainer,
Typography,
RetentionFieldInputContainer,
RetentionFieldLabel,
} from './styles';
import {
convertHoursValueToRelevantUnit,
SettingPeriod,
TimeUnits,
} from './utils';
const { Option } = Select;
function Retention({
retentionValue,
setRentionValue,
selectedRetentionPeroid,
setSelectedRetentionPeroid,
setRetentionValue,
text,
}: RetentionProps): JSX.Element {
const options: Option[] = [
{
key: 'hr',
value: 'Hrs',
},
{
key: 'day',
value: 'Days',
},
{
key: 'month',
value: 'Months',
},
];
const onClickHandler = (
e: { key: string },
func: React.Dispatch<React.SetStateAction<SettingPeroid>>,
): void => {
const selected = e.key as SettingPeroid;
func(selected);
};
const menu = (
<Menu onClick={(e): void => onClickHandler(e, setSelectedRetentionPeroid)}>
{options.map((option) => (
<Menu.Item key={option.key}>{option.value}</Menu.Item>
))}
</Menu>
hide,
}: RetentionProps): JSX.Element | null {
const {
value: initialValue,
timeUnitValue: initialTimeUnitValue,
} = convertHoursValueToRelevantUnit(Number(retentionValue));
const [selectedTimeUnit, setSelectTimeUnit] = useState(initialTimeUnitValue);
const [selectedValue, setSelectedValue] = useState<number | null>(
initialValue,
);
const interacted = useRef(false);
useEffect(() => {
if (!interacted.current) setSelectedValue(initialValue);
}, [initialValue]);
const currentSelectedOption = (option: SettingPeroid): string | undefined => {
return options.find((e) => e.key === option)?.value;
useEffect(() => {
if (!interacted.current) setSelectTimeUnit(initialTimeUnitValue);
}, [initialTimeUnitValue]);
const menuItems = TimeUnits.map((option) => (
<Option key={option.value} value={option.value}>
{option.key}
</Option>
));
const currentSelectedOption = (option: SettingPeriod): void => {
const selectedValue = find(TimeUnits, (e) => e.value === option)?.value;
if (selectedValue) setSelectTimeUnit(selectedValue);
};
useEffect(() => {
const inverseMultiplier = find(
TimeUnits,
(timeUnit) => timeUnit.value === selectedTimeUnit,
)?.multiplier;
if (!selectedValue) setRetentionValue(null);
if (selectedValue && inverseMultiplier) {
setRetentionValue(selectedValue * (1 / inverseMultiplier));
}
}, [selectedTimeUnit, selectedValue, setRetentionValue]);
const onChangeHandler = (
e: React.ChangeEvent<HTMLInputElement>,
func: React.Dispatch<React.SetStateAction<string>>,
func: React.Dispatch<React.SetStateAction<number | null>>,
): void => {
interacted.current = true;
const { value } = e.target;
const integerValue = parseInt(value, 10);
if (value.length > 0 && integerValue.toString() === value) {
const parsedValue = Math.abs(integerValue).toString();
const parsedValue = Math.abs(integerValue);
func(parsedValue);
}
if (value.length === 0) {
func('');
func(null);
}
};
if (hide) {
return null;
}
return (
<RetentionContainer>
<TextContainer>
<Typography>{text}</Typography>
</TextContainer>
<Input
value={retentionValue}
onChange={(e): void => onChangeHandler(e, setRentionValue)}
/>
<Dropdown overlay={menu}>
<Button>
{currentSelectedOption(selectedRetentionPeroid)} <DownOutlined />
</Button>
</Dropdown>
<Row justify="space-between">
<Col flex={1} style={{ display: 'flex' }}>
<RetentionFieldLabel>{text}</RetentionFieldLabel>
</Col>
<Col flex="150px">
<RetentionFieldInputContainer>
<Input
value={selectedValue && selectedValue >= 0 ? selectedValue : ''}
onChange={(e): void => onChangeHandler(e, setSelectedValue)}
style={{ width: 75 }}
/>
<Select
value={selectedTimeUnit}
onChange={currentSelectedOption}
style={{ width: 100 }}
>
{menuItems}
</Select>
</RetentionFieldInputContainer>
</Col>
</Row>
</RetentionContainer>
);
}
interface Option {
key: SettingPeroid;
value: string;
}
interface RetentionProps {
retentionValue: string;
retentionValue: number | null;
text: string;
setRentionValue: React.Dispatch<React.SetStateAction<string>>;
selectedRetentionPeroid: SettingPeroid;
setSelectedRetentionPeroid: React.Dispatch<
React.SetStateAction<SettingPeroid>
>;
setRetentionValue: React.Dispatch<React.SetStateAction<number | null>>;
hide: boolean;
}
export default Retention;

View File

@ -1,43 +1,69 @@
import { Button, Modal, notification, Typography } from 'antd';
import getRetentionperoidApi from 'api/settings/getRetention';
import { Button, Col, Modal, notification, Row, 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 convertIntoHr from 'lib/convertIntoHr';
import getSettingsPeroid from 'lib/getSettingsPeroid';
import React, { useCallback, useEffect, useState } from 'react';
import { find } from 'lodash-es';
import React, { useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { IDiskType } from 'types/api/disks/getDisks';
import { PayloadProps } from 'types/api/settings/getRetention';
import Retention from './Retention';
import {
ButtonContainer,
Container,
ErrorText,
ErrorTextContainer,
ToolTipContainer,
} from './styles';
function GeneralSettings(): JSX.Element {
const [
selectedMetricsPeroid,
setSelectedMetricsPeroid,
] = useState<SettingPeroid>('month');
const { t } = useTranslation();
const [notifications, Element] = notification.useNotification();
const [retentionPeroidMetrics, setRetentionPeroidMetrics] = useState<string>(
'',
);
const [modal, setModal] = useState<boolean>(false);
const [postApiLoading, setPostApiLoading] = useState<boolean>(false);
const [selectedTracePeroid, setSelectedTracePeroid] = useState<SettingPeroid>(
'hr',
);
const [availableDisks, setAvailableDisks] = useState<IDiskType[] | null>(null);
const [retentionPeroidTrace, setRetentionPeroidTrace] = useState<string>('');
const [isDefaultMetrics, setIsDefaultMetrics] = useState<boolean>(false);
const [isDefaultTrace, setIsDefaultTrace] = useState<boolean>(false);
useEffect(() => {
getDisks().then((response) => setAvailableDisks(response.payload));
}, []);
const { payload: currentTTLValues, loading, error, errorMessage } = useFetch<
PayloadProps,
undefined
>(getRetentionPeriodApi, undefined);
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,
);
}
}, [currentTTLValues]);
const onModalToggleHandler = (): void => {
setModal((modal) => !modal);
@ -47,142 +73,182 @@ function GeneralSettings(): JSX.Element {
onModalToggleHandler();
}, []);
const { payload, loading, error, errorMessage } = useFetch<
PayloadProps,
undefined
>(getRetentionperoidApi, undefined);
const s3Enabled = useMemo(
() => !!find(availableDisks, (disks: IDiskType) => disks?.type === 's3'),
[availableDisks],
);
const checkMetricTraceDefault = (trace: number, metric: number): void => {
if (metric === -1) {
setIsDefaultMetrics(true);
} else {
setIsDefaultMetrics(false);
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 (
<Col flex="40%" style={{ minWidth: 475 }} key={category.name}>
<Typography.Title level={3}>{category.name}</Typography.Title>
{category.retentionFields.map((retentionField) => (
<Retention
key={retentionField.name}
text={retentionField.name}
retentionValue={retentionField.value}
setRetentionValue={retentionField.setValue}
hide={!!retentionField.hide}
/>
))}
</Col>
);
}
if (trace === -1) {
setIsDefaultTrace(true);
} else {
setIsDefaultTrace(false);
}
};
useEffect(() => {
if (!loading && payload !== undefined) {
const {
metrics_ttl_duration_hrs: metricTllDuration,
traces_ttl_duration_hrs: traceTllDuration,
} = payload;
checkMetricTraceDefault(traceTllDuration, metricTllDuration);
const traceValue = getSettingsPeroid(traceTllDuration);
const metricsValue = getSettingsPeroid(metricTllDuration);
setRetentionPeroidTrace(traceValue.value.toString());
setSelectedTracePeroid(traceValue.peroid);
setRetentionPeroidMetrics(metricsValue.value.toString());
setSelectedMetricsPeroid(metricsValue.peroid);
}
}, [setSelectedMetricsPeroid, loading, payload]);
return null;
});
const onOkHandler = async (): Promise<void> => {
try {
setPostApiLoading(true);
const retentionTraceValue =
retentionPeroidTrace === '0' && (payload?.traces_ttl_duration_hrs || 0) < 0
? payload?.traces_ttl_duration_hrs || 0
: parseInt(retentionPeroidTrace, 10);
const retentionMetricsValue =
retentionPeroidMetrics === '0' &&
(payload?.metrics_ttl_duration_hrs || 0) < 0
? payload?.metrics_ttl_duration_hrs || 0
: parseInt(retentionPeroidMetrics, 10);
const [tracesResponse, metricsResponse] = await Promise.all([
const [metricsTTLApiResponse, tracesTTLApiResponse] = await Promise.all([
setRetentionApi({
duration: `${convertIntoHr(retentionTraceValue, selectedTracePeroid)}h`,
type: 'traces',
type: 'metrics',
totalDuration: `${metricsTotalRetentionPeriod || -1}h`,
coldStorage: s3Enabled ? 's3' : null,
toColdDuration: `${metricsS3RetentionPeriod || -1}h`,
}),
setRetentionApi({
duration: `${convertIntoHr(
retentionMetricsValue,
selectedMetricsPeroid,
)}h`,
type: 'metrics',
type: 'traces',
totalDuration: `${tracesTotalRetentionPeriod || -1}h`,
coldStorage: s3Enabled ? 's3' : null,
toColdDuration: `${tracesS3RetentionPeriod || -1}h`,
}),
]);
[
{
apiResponse: metricsTTLApiResponse,
name: 'metrics',
},
{
apiResponse: tracesTTLApiResponse,
name: 'traces',
},
].forEach(({ apiResponse, name }) => {
if (apiResponse.statusCode === 200) {
notifications.success({
message: 'Success!',
placement: 'topRight',
if (
tracesResponse.statusCode === 200 &&
metricsResponse.statusCode === 200
) {
notifications.success({
message: 'Success!',
placement: 'topRight',
description: 'Congrats. The retention periods were updated correctly.',
});
checkMetricTraceDefault(retentionTraceValue, retentionMetricsValue);
onModalToggleHandler();
} else {
notifications.error({
message: 'Error',
description:
'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io',
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:
'There was an issue in changing the retention period. Please try again or reach out to support@signoz.io',
description: t('settings.retention_failed_message'),
placement: 'topRight',
});
}
setModal(false);
};
const [isDisabled, errorText] = useMemo(() => {
// 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');
}
}
return [isDisabled, errorText];
}, [
metricsS3RetentionPeriod,
metricsTotalRetentionPeriod,
s3Enabled,
t,
tracesS3RetentionPeriod,
tracesTotalRetentionPeriod,
]);
if (error) {
return <Typography>{errorMessage}</Typography>;
}
if (loading || payload === undefined) {
if (loading || currentTTLValues === undefined) {
return <Spinner tip="Loading.." height="70vh" />;
}
const getErrorText = (): string => {
const getValue = (value: string): string =>
`Retention Peroid for ${value} is not set yet. Please set by choosing below`;
if (!isDefaultMetrics && !isDefaultTrace) {
return '';
}
if (isDefaultMetrics && !isDefaultTrace) {
return `${getValue('Metrics')}`;
}
if (!isDefaultMetrics && isDefaultTrace) {
return `${getValue('Trace')}`;
}
return `${getValue('Trace , Metrics')}`;
};
const isDisabledHandler = (): boolean => {
return !!(retentionPeroidTrace === '' || retentionPeroidMetrics === '');
};
const errorText = getErrorText();
return (
<Container>
<Col xs={24} md={22} xl={20} xxl={18} style={{ margin: 'auto' }}>
{Element}
{errorText ? (
<ErrorTextContainer>
<ErrorText>{errorText}</ErrorText>
@ -204,25 +270,10 @@ function GeneralSettings(): JSX.Element {
/>
</ToolTipContainer>
)}
<Retention
text="Retention Period for Metrics"
selectedRetentionPeroid={selectedMetricsPeroid}
setRentionValue={setRetentionPeroidMetrics}
retentionValue={retentionPeroidMetrics}
setSelectedRetentionPeroid={setSelectedMetricsPeroid}
/>
<Retention
text="Retention Period for Traces"
selectedRetentionPeroid={selectedTracePeroid}
setRentionValue={setRetentionPeroidTrace}
retentionValue={retentionPeroidTrace}
setSelectedRetentionPeroid={setSelectedTracePeroid}
/>
<Row justify="space-around">{renderConfig}</Row>
<Modal
title="Are you sure you want to change the retention period?"
title={t('settings.retention_confirmation')}
focusTriggerAfterClose
forceRender
destroyOnClose
@ -233,24 +284,18 @@ function GeneralSettings(): JSX.Element {
visible={modal}
confirmLoading={postApiLoading}
>
<Typography>
This will change the amount of storage needed for saving metrics & traces.
</Typography>
<Typography>{t('settings.retention_confirmation_description')}</Typography>
</Modal>
<ButtonContainer>
<Button
onClick={onClickSaveHandler}
disabled={isDisabledHandler()}
type="primary"
>
<Button onClick={onClickSaveHandler} disabled={isDisabled} type="primary">
Save
</Button>
</ButtonContainer>
</Container>
</Col>
);
}
export type SettingPeroid = 'hr' | 'day' | 'month';
export type SettingPeriod = 'hr' | 'day' | 'month';
export default GeneralSettings;

View File

@ -1,16 +1,13 @@
import {
Col,
Dropdown as DropDownComponent,
Input as InputComponent,
Typography as TypographyComponent,
} from 'antd';
import styled from 'styled-components';
export const RetentionContainer = styled.div`
width: 50%;
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
export const RetentionContainer = styled(Col)`
margin: 0.75rem 0;
`;
export const Input = styled(InputComponent)`
@ -37,13 +34,6 @@ export const ButtonContainer = styled.div`
}
`;
export const Container = styled.div`
&&& {
display: flex;
flex-direction: column;
}
`;
export const Dropdown = styled(DropDownComponent)`
&&& {
display: flex;
@ -90,3 +80,12 @@ export const ErrorText = styled(TypographyComponent)`
font-style: italic;
}
`;
export const RetentionFieldLabel = styled(TypographyComponent)`
vertical-align: middle;
white-space: pre-wrap;
`;
export const RetentionFieldInputContainer = styled.div`
display: inline-flex;
`;

View File

@ -0,0 +1,42 @@
export type SettingPeriod = 'hr' | 'day' | 'month';
export interface ITimeUnit {
value: SettingPeriod;
key: string;
multiplier: number;
}
export const TimeUnits: ITimeUnit[] = [
{
value: 'hr',
key: 'Hours',
multiplier: 1,
},
{
value: 'day',
key: 'Days',
multiplier: 1 / 24,
},
{
value: 'month',
key: 'Months',
multiplier: 1 / (24 * 30),
},
];
export const convertHoursValueToRelevantUnit = (
value: number,
): { value: number; timeUnitValue: SettingPeriod } => {
if (value)
for (let idx = TimeUnits.length - 1; idx >= 0; idx -= 1) {
const timeUnit = TimeUnits[idx];
const convertedValue = timeUnit.multiplier * value;
if (
convertedValue >= 1 &&
convertedValue === parseInt(`${convertedValue}`, 10)
) {
return { value: convertedValue, timeUnitValue: timeUnit.value };
}
}
return { value, timeUnitValue: TimeUnits[0].value };
};

View File

@ -1,6 +1,6 @@
import { SettingPeroid } from 'container/GeneralSettings';
import { SettingPeriod } from 'container/GeneralSettings';
const converIntoHr = (value: number, peroid: SettingPeroid): number => {
const converIntoHr = (value: number, peroid: SettingPeriod): number => {
if (peroid === 'day') {
return value * 24;
}

View File

@ -1,4 +1,4 @@
import { SettingPeroid } from 'container/GeneralSettings';
import { SettingPeriod } from 'container/GeneralSettings';
const getSettingsPeroid = (hr: number): PayloadProps => {
if (hr <= 0) {
@ -30,7 +30,7 @@ const getSettingsPeroid = (hr: number): PayloadProps => {
interface PayloadProps {
value: number;
peroid: SettingPeroid;
peroid: SettingPeriod;
}
export default getSettingsPeroid;

View File

@ -5,29 +5,32 @@ import CreateAlertChannels from 'container/CreateAlertChannels';
import GeneralSettings from 'container/GeneralSettings';
import history from 'lib/history';
import React from 'react';
import { useTranslation } from 'react-i18next';
function SettingsPage(): JSX.Element {
const pathName = history.location.pathname;
const { t } = useTranslation();
return (
<RouteTab
{...{
routes: [
{
Component: GeneralSettings,
name: 'General Settings',
name: t('routes.general'),
route: ROUTES.SETTINGS,
},
{
Component: (): JSX.Element => {
return <CreateAlertChannels preType="slack" />;
},
name: 'Alert Channels',
name: t('routes.alert_channels'),
route: ROUTES.ALL_CHANNELS,
},
],
activeKey:
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
pathName === ROUTES.SETTINGS
? t('routes.general')
: t('routes.alert_channels'),
}}
/>
);

View File

@ -4,27 +4,30 @@ import AlertChannels from 'container/AllAlertChannels';
import GeneralSettings from 'container/GeneralSettings';
import history from 'lib/history';
import React from 'react';
import { useTranslation } from 'react-i18next';
function AllAlertChannels(): JSX.Element {
const pathName = history.location.pathname;
const { t } = useTranslation();
return (
<RouteTab
{...{
routes: [
{
Component: GeneralSettings,
name: 'General Settings',
name: t('routes.general'),
route: ROUTES.SETTINGS,
},
{
Component: AlertChannels,
name: 'Alert Channels',
name: t('routes.alert_channels'),
route: ROUTES.ALL_CHANNELS,
},
],
activeKey:
pathName === ROUTES.SETTINGS ? 'General Settings' : 'Alert Channels',
pathName === ROUTES.SETTINGS
? t('routes.general')
: t('routes.alert_channels'),
}}
/>
);

View File

@ -14,7 +14,7 @@ function SettingsPage(): JSX.Element {
routes: [
{
Component: GeneralSettings,
name: 'General Settings',
name: 'General',
route: ROUTES.SETTINGS,
},
{
@ -23,8 +23,7 @@ function SettingsPage(): JSX.Element {
route: ROUTES.ALL_CHANNELS,
},
],
activeKey:
pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General Settings',
activeKey: pathName === ROUTES.ALL_CHANNELS ? 'Alert Channels' : 'General',
}}
/>
);

View File

@ -0,0 +1,5 @@
export type PayloadProps = IDiskType[];
export interface IDiskType {
name: string;
type: string;
}

View File

@ -1,4 +1,6 @@
export interface PayloadProps {
metrics_ttl_duration_hrs: number;
metrics_move_ttl_duration_hrs?: number;
traces_ttl_duration_hrs: number;
traces_move_ttl_duration_hrs?: number;
}

View File

@ -1,6 +1,8 @@
export interface Props {
type: 'metrics' | 'traces';
duration: string;
totalDuration: string;
coldStorage?: 's3' | null;
toColdDuration?: string;
}
export interface PayloadProps {